升级并完成 Razor Pages + Blazor Web App 骨架。

- 项目现为 net10.0,使用本机 .NET SDK 10.0.400 / runtime 10.0.11。
- MySQL 驱动当前最新兼容版本是 Pomelo 9.0,因此 EF Core 保持在 9.0.11;迁移已可正常读取。
- Razor Pages 静态 SSR:
  - [公开首页](E:\\election system\\src\\ElectionSystem.Api\\Pages\\Index.cshtml)
  - [公开公告示例](E:\\election system\\src\\ElectionSystem.Api\\Pages\\Content\\Notice.cshtml)
- Blazor Interactive Server:
  - [投票交互预览](E:\\election system\\src\\ElectionSystem.Api\\Components\\Pages\\BallotPreview.razor),仅演示交互,不提交或保存选票。
- 已加入 Blazor 所需的反伪造中间件,架构说明同步更新:[architecture.md](E:\\election system\\docs\\architecture.md)。
This commit is contained in:
biss committed 2026-08-29 11:31:28 +08:00
1 parent ab935480b0
commit 2eddb1217e
17 files changed
+251 -9

No files matched your search

+2
View File
@@ -4,6 +4,8 @@
`Identity` 仅维护本地用户与未来外部身份提供商的映射;`Organization` 维护组织树和成员范围;`Election`、`Voting`、`OfflineCounting`、`Cms`、`PublicBoard` 和 `Audit` 预留为独立业务模块。 `Identity` 仅维护本地用户与未来外部身份提供商的映射;`Organization` 维护组织树和成员范围;`Election`、`Voting`、`OfflineCounting`、`Cms`、`PublicBoard` 和 `Audit` 预留为独立业务模块。
公开内容采用 Razor Pages 的静态服务端渲染;需要即时校验和状态更新的区域采用 Blazor Web App 的 Interactive Server 渲染。交互组件不能承担投票防重、资格或计票规则,这些规则仍必须由服务端事务和数据库约束执行。
线上投票实现时,资格/已投状态与匿名选票将使用不同实体和数据库约束,禁止常规查询将选民反向关联到选票内容。本阶段没有创建选票表,因此不会产生该关联。 线上投票实现时,资格/已投状态与匿名选票将使用不同实体和数据库约束,禁止常规查询将选民反向关联到选票内容。本阶段没有创建选票表,因此不会产生该关联。
## MySQL 配置 ## MySQL 配置
+1 -1
View File
@@ -3,7 +3,7 @@
"isRoot": true, "isRoot": true,
"tools": { "tools": {
"dotnet-ef": { "dotnet-ef": {
"version": "8.0.17", "version": "9.0.11",
"commands": [ "commands": [
"dotnet-ef" "dotnet-ef"
], ],
+59
View File
@@ -0,0 +1,59 @@
$ErrorActionPreference = 'Stop'
$psi = [System.Diagnostics.ProcessStartInfo]::new()
$psi.FileName = 'dotnet'
$psi.UseShellExecute = $false
$psi.RedirectStandardOutput = $true
$psi.RedirectStandardError = $true
$psi.Environment['ASPNETCORE_ENVIRONMENT'] = 'Development'
$psi.Environment['Security__EnableHttpsRedirection'] = 'false'
$argList = @(
'run', '--no-build', '--configuration', 'Release', '--project', 'src/ElectionSystem.Api',
'--', '--urls', 'http://127.0.0.1:5055'
)
foreach ($arg in $argList) {
$null = $psi.ArgumentList.Add($arg)
}
$process = [System.Diagnostics.Process]::Start($psi)
try {
$ready = $false
for ($i = 0; $i -lt 20; $i++) {
Start-Sleep -Milliseconds 500
try {
$response = Invoke-WebRequest -Uri 'http://127.0.0.1:5055/' -UseBasicParsing -TimeoutSec 2
if ($response.StatusCode -eq 200) {
$ready = $true
break
}
} catch { }
}
if (-not $ready) {
throw "Application did not become ready. $($process.StandardError.ReadToEnd())"
}
$homeResponse = Invoke-WebRequest -Uri 'http://127.0.0.1:5055/' -UseBasicParsing -TimeoutSec 5
$notice = Invoke-WebRequest -Uri 'http://127.0.0.1:5055/content/notice' -UseBasicParsing -TimeoutSec 5
$interactive = Invoke-WebRequest -Uri 'http://127.0.0.1:5055/ballot-preview' -UseBasicParsing -TimeoutSec 5
if ($homeResponse.Content -notmatch '公开内容以服务端渲染快速交付') { throw 'Home page content check failed.' }
if ($notice.Content -notmatch '公开公告模板') { throw 'Notice page content check failed.' }
if ($interactive.Content -notmatch '投票交互预览') { throw 'Interactive component content check failed.' }
"Runtime page checks passed: / ($($homeResponse.StatusCode)), /content/notice ($($notice.StatusCode)), /ballot-preview ($($interactive.StatusCode))"
} catch {
if (-not $process.HasExited) {
$process.Kill($true)
$process.WaitForExit()
}
$serverError = $process.StandardError.ReadToEnd()
$serverOutput = $process.StandardOutput.ReadToEnd()
throw "Runtime verification failed: $($_.Exception.Message)`n$serverError`n$serverOutput"
} finally {
if (-not $process.HasExited) {
$process.Kill($true)
$process.WaitForExit()
}
}
Loaded 3 of 17 files, more files were not shown because too many files have changed in this diff. Show more