信息比对助手
本文档介绍了信息比对助手项目的技术架构与核心业务逻辑。当前实现采用 Blazor Server + Photino.NET 桌面窗口,内嵌 Kestrel 服务,使用 SQLite 数据库和 SqlSugar ORM,支持 Excel 导入导出。核心流程包括:以身份证号为主键维护底册,导入外部死亡名单并进行唯一性校验,执行比对并生成结果,支持确认注销状态,可导出结果,并在应用退出时尝试备份数据库。
信息比对助手 — 项目规划与实现说明
本文档在原规划基础上,补充当前已实现架构与数据库各表字段说明,便于对照代码(InfoCompareAssistant/Data/Entities.cs)进行检查与调整。
一、技术架构选型(规划 + 当前实现)
1.1 规划目标
- 统一技术栈、支持国产化与离线场景。
- 桌面端轻量、本地 SQLite、大批量 Excel 友好。
1.2 当前实现(与规划的差异说明)
| 维度 | 原规划 | 当前实现 | 说明 |
|---|---|---|---|
| 客户端 | Blazor Hybrid + Photino(偏 WASM 宿主) | Blazor Server + Photino.NET 打开窗口,内嵌本机 Kestrel 页面 | 数据访问(SqlSugar + SQLite)在完整 CLR 上运行,避免纯 WASM 下 SQLite 能力受限;界面仍为 Blazor + WebView 体验。 |
| UI | Blazor + Tailwind CSS | Blazor + 自研主题 CSS(蓝/绿柔和卡片风格) | 视觉目标与规划一致;若需严格 Tailwind 构建链,可后续接入 npm/postcss。 |
| 数据库 | SQLite | SQLite,文件路径:%LocalApplicationData%\InfoCompareAssistant\app.db |
随用户目录持久化。 |
| ORM | SqlSugar | SqlSugar(CodeFirst 建表) | 实体见下文与 Entities.cs。 |
| Excel | MiniExcel | MiniExcel | 底册导入、死亡名单导入、结果导出。 |
| 监听端口 | — | http://127.0.0.1:0(系统分配空闲端口) |
避免固定端口被占用导致启动失败;Photino 加载地址为启动后解析的实际 URL。 |
1.3 依赖包(实现侧)
- Photino.NET:桌面窗口承载 WebView。
- SqlSugarCore + Microsoft.Data.Sqlite:SQLite 访问与 ORM。
- MiniExcel:流式/低内存 Excel 读写。
二、核心业务逻辑(与实现对齐)
底册(特殊人员)
以身份证号为底册主键;支持单条维护与 Excel 批量导入(按身份证号 upsert)。外部死亡名单
每次向导导入形成一批 DeathRecord 行,并与历史做 身份证号规范化 + 死亡日期 Key 的全局唯一校验(见数据库索引),防止重复导入。比对
将本批死亡记录与 PersonRegistry 按规范化身份证号关联;底册状态**不等于「已故」**的命中写入 CompareMatch,供界面展示与确认。确认注销
对选中/一键确认的比对结果:底册对应人员 Status → 已故,CancelDate → 业务当天(日期);比对行 Confirmed / ConfirmedAt 更新。导出
按批次导出比对结果为 Excel(HTTP:/api/export/{batchId})。退出备份
应用停止时尝试将app.db复制到D:\数据备份(目录不存在则创建;无 D 盘或无权限则静默失败,不阻断退出)。
三、界面与交互(规划要点)
- 首页:在册总数、本月待处理预警、上次比对完成时间;快捷入口。
- 一键比对向导:上传 → 列映射 → 比对进度 → 结果表(未确认高亮)、确认注销、导出。
- 人员名册:网格/类别筛选、分页、单条编辑、Excel 导入。
四、防呆与容错(规划 + 实现要点)
- 身份证:去控制字符与空白、大写、15 位升 18 位、展示与匹配以规范化字段为准(底册主键与死亡表 IdNumberNorm)。
- 死亡日期:同时存 DeathDateKey(用于唯一性与关联)与 DeathDateDisplay(展示原文或可解析日期)。
- 重复导入:数据库层 DeathRecord(IdNumberNorm, DeathDateKey) 唯一索引 + 业务层导入前校验。
- 备份:见第二节「退出备份」。
五、数据库表结构说明(详细字段)
以下与 SQLite 中实际表名、字段一致,对应 C# 类在命名空间 InfoCompareAssistant.Data。
时间字段:代码中写入多为 UTC(DateTime.UtcNow),界面展示可按本地时区转换;CancelDate 为业务意义上的「注销日期」,使用 日期(当天 DateTime.Today)。
5.1 表 PersonRegistry(特殊人员底册)
用途:在册人员主数据;业务上以身份证号唯一标识一人。
| 字段名 | 类型(C# / 逻辑) | 可空 | 约束 | 说明 |
|---|---|---|---|---|
| IdNumber | string |
否 | 主键,长度约 32 | 身份证号,存规范化后值(清洗 + 15 转 18 + 大写等),与业务「一人一号」一致。 |
| Name | string |
否 | 长度约 64 | 姓名。 |
| Contact | string |
是 | 长度约 64 | 联系方式(电话等)。 |
| GridCommunity | string |
是 | 长度约 128 | 管辖网格 / 社区。 |
| SpecialCategory | string |
是 | 长度约 64 | 特殊类别(低保、残疾、重病等),可自定义文案。 |
| Status | string |
否 | 长度约 16,默认 "正常" |
人员状态;约定取值:正常、已故、迁出(与比对、筛选逻辑一致,修改时请全局检索)。 |
| CancelDate | DateTime? |
是 | — | 注销日期;在「确认并注销底册人员」时写入,一般为操作当日日期。 |
| CreatedAt | DateTime |
否 | — | 首次写入时间(UTC)。 |
| UpdatedAt | DateTime |
否 | — | 最后更新时间(UTC)。 |
检查/调整建议:若需增加字段(如备注、照片路径),在本表扩展后同步:名册页表单、RegistryService、底册 Excel 导入列识别逻辑。
5.2 表 DeathRecord(外部死亡名单导入明细)
用途:每次向导导入的一批死亡行;全局用于「身份证号 + 死亡日期」重复拦截(与批次无关,历史任一导入占坑后即不可再导入相同组合)。
| 字段名 | 类型 | 可空 | 约束 | 说明 |
|---|---|---|---|---|
| Id | int |
否 | 主键,自增 | SQLite 下为 INTEGER PRIMARY KEY 自增行标识。 |
| BatchId | int |
否 | 逻辑外键 → CompareBatch.Id |
所属导入批次。 |
| IdNumberNorm | string |
否 | 长度约 32 | 规范化身份证号,与 PersonRegistry.IdNumber 对齐比较。 |
| DeathDateKey | string |
否 | 长度约 32 | 死亡日期规范化键(如 yyyyMMdd 或无法解析时的占位规则),用于唯一性与去重;与 IdNumberNorm 联合唯一。 |
| Name | string |
否 | 长度约 64 | 名单中的姓名(可为空串,视导入数据而定)。 |
| DeathDateDisplay | string |
是 | 长度约 64 | 死亡日期展示用字符串(保留 Excel 原样或格式化可读日期)。 |
| SourceFileName | string |
是 | 长度约 256 | 上传时的原始文件名(审计/追溯)。 |
| ImportedAt | DateTime |
否 | — | 导入时间(UTC)。 |
索引(代码初始化脚本)
ux_death_id_date:唯一索引(IdNumberNorm, DeathDateKey)- 含义:同一规范化身份证 + 同一死亡日期键在全库仅允许一条死亡导入记录。
- 调整注意:修改
DeathDateKey生成规则会导致「是否与历史重复」判定变化,需评估历史数据迁移或重建索引。
5.3 表 CompareBatch(一次比对导入批次)
用途:一次「上传 + 比对」会话的元数据,与多条 DeathRecord、CompareMatch 关联。
| 字段名 | 类型 | 可空 | 约束 | 说明 |
|---|---|---|---|---|
| Id | int |
否 | 主键,自增 | 批次号;导出接口 /api/export/{batchId} 即此 Id。 |
| SourceFileName | string |
是 | 长度约 256 | 本次上传文件名。 |
| StartedAt | DateTime |
否 | — | 批次开始时间(UTC)。 |
| FinishedAt | DateTime |
否 | — | 批次结束时间(UTC),比对写库完成后更新。 |
| RowCount | int |
否 | — | 本批解析到的有效死亡行数(业务统计)。 |
| MatchCount | int |
否 | — | 本批产生的「待处理/已展示」命中条数(CompareMatch 行数)。 |
检查/调整建议:若需记录操作员、机构、备注等,可在本表增加字段并在 CompareFlowService.ImportAndCompare 中赋值。
5.4 表 CompareMatch(比对命中结果)
用途:本批中「底册存在且导入当时底册状态非已故」的命中记录;支持确认后写回底册并标记本行已确认。
| 字段名 | 类型 | 可空 | 约束 | 说明 |
|---|---|---|---|---|
| Id | int |
否 | 主键,自增 | 单行比对结果标识。 |
| BatchId | int |
否 | 逻辑外键 → CompareBatch.Id |
所属批次。 |
| IdNumberNorm | string |
否 | 长度约 32 | 规范化身份证号,对应底册人员。 |
| Name | string |
否 | 长度约 64 | 展示姓名(优先名单姓名,缺省用底册姓名)。 |
| DeathDateDisplay | string |
是 | 长度约 64 | 死亡日期展示。 |
| RegistryStatus | string |
否 | 长度约 16 | 写入比对结果时底册中的 Status 快照(用于审计「当时为何预警」)。 |
| GridCommunity | string |
是 | 长度约 128 | 底册管辖网格/社区快照。 |
| SpecialCategory | string |
是 | 长度约 64 | 底册特殊类别快照。 |
| Contact | string |
是 | 长度约 64 | 底册联系方式快照。 |
| Confirmed | bool |
否 | 默认 false |
是否已在界面执行「确认注销」处理。 |
| ConfirmedAt | DateTime? |
是 | — | 确认操作时间(UTC)。 |
| CreatedAt | DateTime |
否 | — | 本行生成时间(UTC)。 |
检查/调整建议:若需保留「确认人」或「二次复核」状态,可增加字段并扩展确认接口。
5.5 表关系简图(逻辑)
CompareBatch (1) ──< (N) DeathRecord # 一批导入多条死亡行
CompareBatch (1) ──< (N) CompareMatch # 一批产生多条命中(仅非已故底册)
PersonRegistry (1) ← 按 IdNumberNorm 关联 ─ DeathRecord / CompareMatch(比对逻辑)
说明:DeathRecord 与 PersonRegistry 在库中无强制外键声明,由业务代码关联;删除底册人员前需评估是否保留历史 DeathRecord / CompareMatch。
六、运行与调试(补充)
- 默认桌面:
dotnet run(启动配置 Photino)。 - 仅浏览器:
dotnet run --launch-profile 浏览器调试,按控制台打印的实际http://127.0.0.1:端口打开。 - 强制浏览器、不启 Photino:
dotnet run -- --browser。
七、后续可优化方向(可选)
- 死亡/底册数据量在 十万级以上 时:可对导入校验、命中查询做分批或索引优化,并评估 MiniExcel 全量入内存策略。
- 严格接入 Tailwind 构建与 design token。
- 表结构变更时:同步更新本文档第五节与
DatabaseInitializer中的手工 SQL(若有)。
文档版本与实现对齐;修改表字段后请同时更新 Data/Entities.cs 与本节内容。