本文引用的文件
本文面向调查分析场景中的数据库设计,重点说明三类数据:
table_info 与 table_field(通过 external_table_field.sql 注入)描述外部表的中文名称、英文表名、分类、字段映射和排序。仓库中 sql/ 目录下的 SQL 脚本是本次设计的权威来源;其中 case_table.sql、case_table_1.sql、external_tables.sql、external_table_field.sql 构成主体,table_data_1.sql 提供一条配置样例,user.sql 提供平台用户与案件归属迁移逻辑。
本设计文档仅覆盖以下 SQL 文件对应的表和元数据:
| 文件 | 主要职责 |
|---|---|
sql/case_table.sql |
案件内部业务表及索引,包括交易、通话、账号开户、快递、酒店、航班、火车、同住同乘、时序表等 |
sql/case_table_1.sql |
案件支撑表,包括文件信息、AI 非表格档案、基础人员、人员分组、关系节点边、治理树、配置、错误日志、图谱历史等 |
sql/external_tables.sql |
13 张外部数据表定义,如出入境证件、出入境记录、机动车、驾驶证、车辆违章、同户人、同住址、违法犯罪人员、在逃人员等 |
sql/external_table_field.sql |
外部表在 table_info、table_field 中的注册与字段映射插入语句 |
sql/table_data_1.sql |
治理配置样例,例如是否启用“开卡信息” |
sql/user.sql |
平台用户表、初始管理员、以及 case_info.owner_id 归属迁移 |
graph TB
A["平台用户<br/>sys_user"] --> B["案件主表<br/>case_info"]
B --> C["文件信息<br/>file_info"]
C --> D["案件内部表<br/>trans_record / call_record / ..."]
B --> E["外部表元数据<br/>table_info / table_field"]
E --> F["外部表<br/>cert_info / entry_rec / vehicle_info / lic_info / veh_violation / household / co_resident / criminal / fugitive / ..."]
C --> G["AI 非表格档案<br/>file_ai_profile"]
B --> H["关系图谱<br/>rel_node / rel_edge / edge_conf / rel_node_conf"]
图表来源
章节来源
当前仓库中的核心实体可划分为四层:
| 层次 | 实体示例 | 作用 |
|---|---|---|
| 平台层 | sys_user |
系统登录与管理员标志,不实现完整权限体系 |
| 案件层 | case_info(由 user.sql 引用)、file_info |
案件主对象及其导入文件元信息 |
| 业务表层 | trans_record、call_record、account_opening_info、hotel_stay_info、flight_ticket_info、train_ticket_info、together_live_info、time_series_info 等 |
案件内结构化调查数据 |
| 外部扩展层 | cert_info、entry_rec、vehicle_info、lic_info、veh_violation、household、co_resident、criminal、fugitive、fugi_revoke、fugi_co、fugi_co_revoke |
公安数据源扩展实体 |
| 元数据层 | table_info、table_field |
外部表的中文名称、英文表名、分类、字段中文名、英文名、类型、必填、排序等 |
需要特别注意:
case_table_1.sql 中的 file_info、person_basic_info、person_group、rel_node、rel_edge 等属于案件内部支撑表。external_tables.sql 中的 13 张表属于外部表定义。external_table_field.sql 不是物理表定义,而是向 table_info、table_field 插入元数据的语句;这些元数据用于 UI 展示、字段匹配和语义检索。章节来源
user.sql 明确说明:平台库使用 PostgreSQL,sys_user 只承担认证和最小权限边界,不实现完整角色权限体系。新增字段 case_info.owner_id 将案件归属到创建者,并通过索引加速按用户查询其案件。
erDiagram
SYS_USER {
bigint id PK
varchar username
varchar pwd_hash
varchar display_name
smallint is_admin
smallint status
timestamp create_time
timestamp update_time
timestamp last_login_time
}
CASE_INFO {
bigint id PK
bigint owner_id FK
}
SYS_USER ||--o{ CASE_INFO : "拥有"
图表来源
file_info 是文件导入的基础元数据表,包含文件名、工作簿名、批次号、模板号、目标表名、行数、成功失败数、状态、哈希值、创建时间等。它通过 pid 关联案件主表(注释中写为 pid,通常对应 case_info.id),从而把上传文件和解析结果归到具体案件。
erDiagram
FILE_INFO {
bigint id PK
bigint pid
varchar fileName
varchar sheetName
bigint batchId
bigint templateId
varchar tableName
integer lineNo
varchar header
varchar password
varchar charSet
varchar delimiter
varchar fileType
varchar filePath
bigint fileSize
integer dataNum
integer successNum
integer failNum
varchar fileStatus
varchar md5
timestamp createDateTime
}
CASE_INFO {
bigint id PK
}
CASE_INFO ||--o{ FILE_INFO : "包含文件"
图表来源
大多数案件内部表都有 fileId 和 sheetId,表示数据来源文件和工作簿页签。这是跨表定位原始数据的关键外键语义,即使 SQL 中没有显式声明 FOREIGN KEY。
章节来源
trans_record:交易明细。关键字段包括 personName、personCardNo、personAccount、otherName、otherCardNo、transAmount、transDateTime、transSerialNo 等。trans_record_all:全量或聚合后的交易表,字段结构与明细表基本一致。personName、personCardNo、otherName、otherCardNo 建索引;时间、金额等分析过滤依赖 DuckDB zonemap,不额外建索引。call_record:通话记录。关键字段包括 personPhone、personCertNo、otherPhone、callDateTime、duration、callDirection、callType 等。call_record_all:通话全量表。use_open_info_call_record:基于开卡信息的通话记录。call_opening_info:电话开户信息,包含运营商、号码状态、激活时间、首次通话时间等。| 表名 | 业务含义 | 关键外键语义 |
|---|---|---|
account_opening_info |
银行账号开户信息 | fileId、sheetId、personName、personCardNo |
express_info |
快递收发件信息 | fileId、sheetId、收件寄件人姓名、电话、运单号 |
resident_population_info |
常住人口信息 | fileId、sheetId、身份证号、姓名、地址 |
hotel_stay_info |
酒店住宿信息 | fileId、sheetId、姓名、身份证号、入住离店时间 |
flight_ticket_info |
航班订票信息 | fileId、sheetId、姓名、身份证号、航班号、票价 |
flight_departure_info |
航班出发信息 | fileId、sheetId、航班号、旅客信息 |
together_flight_info |
同航班服务 | fileId、sheetId、航班号、同行人 |
together_train_info |
同火车服务 | fileId、sheetId、车次、车厢座位、同行人 |
train_ticket_info |
火车票订票信息 | fileId、sheetId、车次、出发到达、座位 |
together_live_info |
同住服务 | fileId、sheetId、被查询人与同住人 |
track_car_ticket_info |
汽车票务联网售票记录 | fileId、sheetId、车牌、乘客、发车到达 |
time_series_info |
时序汇总表 | tableNameEn、tableNameCn、人物、时间、扩展字段 |
flowchart TD
Start["导入文件"] --> FileInfo["写入 file_info"]
FileInfo --> Parse["解析并落库到案件内部表"]
Parse --> Trans["交易表 trans_record"]
Parse --> Call["通话表 call_record"]
Parse --> Account["账号开户表 account_opening_info"]
Parse --> Travel["交通出行表 flight/train/car"]
Parse --> Stay["住宿表 hotel_stay_info"]
Parse --> TimeSeries["时序表 time_series_info"]
Trans --> Query["按案件、人物、账号、手机号查询"]
Call --> Query
Account --> Query
Travel --> Query
Stay --> Query
TimeSeries --> Query
图表来源
章节来源
external_tables.sql 定义了 13 张外部数据表,主要用于调查场景中的扩展实体:
| 序号 | 英文表名 | 中文含义 | 主要调查用途 |
|---|---|---|---|
| 1 | cert_info |
出入境证件信息 | 证件持有者身份、签发机关、有效期 |
| 2 | entry_rec |
出入境记录信息 | 出入境口岸、时间、交通工具、签证 |
| 3 | vehicle_info |
机动车信息 | 车辆登记、住所、能源类型、载客载货 |
| 4 | lic_info |
驾驶证信息 | 准驾车型、发证机关、记分、状态 |
| 5 | veh_violation |
机动车违章信息 | 违法时间、地点、处理机关、违法行为 |
| 6 | household |
同户人 | 户籍家庭成员、关系、婚姻、死亡日期 |
| 7 | co_resident |
同住址 | 共同居住人员、民族、职业、婚姻 |
| 8 | co_veh_viol |
同车违章 | 同一车牌多次违章的人员 |
| 9 | criminal |
违法犯罪人员信息 | 别名、性别、身高、体表标记、案件类别 |
| 10 | fugitive |
在逃人员登记信息 | 通缉令编号、立案单位、抓捕级别、奖金 |
| 11 | fugi_revoke |
在逃人员撤销信息 | 撤销人、撤销时间、抓获方式、抓获单位 |
| 12 | fugi_co |
在逃同案人员 | 同案人员登记、审批、立案日期 |
| 13 | fugi_co_revoke |
在逃同案撤销人员 | 撤销流程、抓获地点、案情简要 |
这些外部表普遍包含 id、fileId、sheetId,以及大量以 VARCHAR 存储的公安业务字段。它们通过身份证号、姓名、车牌号、证件号等与案件内部人员、交易、通话、出行数据进行关联。
external_table_field.sql 向 table_info 和 table_field 插入元数据。每条字段记录包含:
table_id:对应 table_info.id。table_name_en:外部表英文名。field_name_cn:字段中文名。field_name_en:字段英文名。field_type:字段类型,当前均为 TEXT。required:是否必填。field_len:长度限制。matched:是否已匹配。direction_conf:方向配置。field_sort:显示顺序。zf:校验或标识字段。table_info 中还包含 classify,当前值为“公安数据库”,以及 has_inner、has_table 等开关,用于区分内部表与外部表、是否已有物理表等。
classDiagram
class TableInfo {
+bigint id
+bigint main_id
+varchar table_name_cn
+varchar table_name_en
+varchar func_regex
+integer line_no
+varchar headers
+varchar classify
+integer has_inner
+integer has_table
+timestamp create_time
}
class TableField {
+bigint table_id
+varchar table_name_en
+varchar field_name_cn
+varchar field_name_en
+varchar field_type
+integer required
+integer field_len
+integer matched
+varchar direction_conf
+integer field_sort
+integer zf
}
TableInfo "1" --> "many" TableField : "字段映射"
图表来源
| 调查维度 | 对应外部表 | 说明 |
|---|---|---|
| 通话 | call_record、call_record_all、use_open_info_call_record、call_opening_info |
通话明细、全量、开卡信息通话、电话开户 |
| 交易 | trans_record、trans_record_all |
个人转账、账户余额、商户、终端、渠道 |
| 轨迹 | flight_ticket_info、flight_departure_info、train_ticket_info、track_car_ticket_info、together_flight_info、together_train_info、time_series_info |
航班、火车、汽车票及同行记录 |
| 人员 | person_basic_info、household、co_resident、criminal、fugitive、fugi_revoke、fugi_co、fugi_co_revoke |
基础人员、同户、同住、违法犯罪、在逃 |
| 车辆违法 | vehicle_info、lic_info、veh_violation、co_veh_viol |
机动车、驾驶证、违章、同车违章 |
| 其他扩展 | cert_info、entry_rec、hotel_stay_info、express_info、resident_population_info |
出入境证件、出入境记录、酒店、快递、人口 |
章节来源
下面给出一个整合用户、案件、文件、内部表、外部表和元数据的实体关系图。注意:当前 SQL 脚本中大部分表之间没有显式 FOREIGN KEY,因此关系更多体现为业务语义而非数据库约束。
erDiagram
SYS_USER {
bigint id PK
varchar username
smallint is_admin
smallint status
}
CASE_INFO {
bigint id PK
bigint owner_id FK
}
FILE_INFO {
bigint id PK
bigint pid
varchar tableName
varchar fileStatus
}
TABLE_INFO {
bigint id PK
varchar table_name_en
varchar table_name_cn
varchar classify
}
TABLE_FIELD {
bigint table_id FK
varchar table_name_en
varchar field_name_cn
varchar field_name_en
}
TRANS_RECORD {
bigint id PK
bigint fileId
bigint sheetId
varchar personName
varchar personCardNo
decimal transAmount
timestamp transDateTime
}
CALL_RECORD {
bigint id PK
bigint fileId
bigint sheetId
varchar personPhone
varchar otherPhone
timestamp callDateTime
}
CERT_INFO {
bigint id PK
bigint fileId
bigint sheetId
varchar cid
varchar certNo
}
ENTRY_REC {
bigint id PK
bigint fileId
bigint sheetId
varchar certNo
varchar ioDate
}
VEHICLE_INFO {
bigint id PK
bigint fileId
bigint sheetId
varchar plateNo
}
LIC_INFO {
bigint id PK
bigint fileId
bigint sheetId
varchar licNo
}
CRIMINAL {
bigint id PK
bigint fileId
bigint sheetId
varchar cid
varchar name
}
FUGITIVE {
bigint id PK
bigint fileId
bigint sheetId
varchar certNo
varchar caseNo
}
SYS_USER ||--o{ CASE_INFO : "owner_id"
CASE_INFO ||--o{ FILE_INFO : "pid"
TABLE_INFO ||--o{ TABLE_FIELD : "table_id"
FILE_INFO ||--o{ TRANS_RECORD : "fileId"
FILE_INFO ||--o{ CALL_RECORD : "fileId"
FILE_INFO ||--o{ CERT_INFO : "fileId"
FILE_INFO ||--o{ ENTRY_REC : "fileId"
FILE_INFO ||--o{ VEHICLE_INFO : "fileId"
FILE_INFO ||--o{ LIC_INFO : "fileId"
FILE_INFO ||--o{ CRIMINAL : "fileId"
FILE_INFO ||--o{ FUGITIVE : "fileId"
图表来源
| 字段 | 类型 | 含义 | 是否常用索引 |
|---|---|---|---|
id |
BIGINT | 主键,部分表使用序列生成 | 主键索引 |
fileId |
BIGINT | 所属文件 ID | 常用于按文件定位,但脚本未统一建索引 |
sheetId |
BIGINT | 工作簿页签 ID | 常用于联合文件定位 |
personName |
VARCHAR | 人员姓名 | 多数表有单独索引 |
personCertNo / cid |
VARCHAR | 身份证号 | 多数表有单独索引 |
personPhone / otherPhone |
VARCHAR | 手机号 | 通话表有索引 |
transDateTime / callDateTime |
TIMESTAMP | 时间字段 | 依赖 zonemap,未额外建索引 |
transAmount / transBalance |
DECIMAL(18,2) | 金额字段 | 依赖 zonemap,未额外建索引 |
idx_trans_record_person_name、idx_trans_record_person_card_no、idx_trans_record_other_name、idx_trans_record_other_card_no。idx_call_record_person_name、idx_call_record_person_phone、idx_call_record_other_name、idx_call_record_other_phone。idx_account_opening_info_person_name、idx_account_opening_info_person_card_no、idx_account_opening_info_phone。idx_call_opening_info_person_phone、idx_call_opening_info_person_name。idx_express_info_recipient_name、idx_express_info_recipient_phone、idx_express_info_sender_phone。idx_resident_population_info_name。idx_hotel_stay_info_person_name、idx_hotel_stay_info_person_cert_no。idx_flight_ticket_info_person_name、idx_flight_ticket_info_person_cert_no。idx_flight_departure_info_person_name。idx_together_flight_info_person_cert_no、idx_together_flight_info_companion_name。idx_together_train_info_person_name、idx_together_train_info_person_cert_no、idx_together_train_info_companion_name。idx_train_ticket_info_person_name。idx_together_live_info_query_cert_no、idx_together_live_info_together_name。idx_track_car_ticket_info_person_name、idx_track_car_ticket_info_person_cert_no。idx_time_series_info_person_name、idx_time_series_info_other_name。uk_sys_user_username。idx_case_info_owner。SQL 注释明确指出 DuckDB 索引约定:
CREATE INDEX 创建的是 ART 索引,适合单列高选择性等值或 IN 查询。这一原则意味着:未来新增索引时,应优先评估是否属于高频定位列,而不是对所有 VARCHAR 列都建索引。
章节来源
通过 file_info.pid 找到案件下的所有文件,再根据 fileId 筛选各案件内部表。外部表也通过 fileId 与同一批导入文件关联。
推荐思路:
file_info,用 pid = ? 获取该案件的所有文件。fileId IN (...) 筛选 trans_record、call_record、hotel_stay_info、flight_ticket_info、train_ticket_info 等。fileId IN (...) 筛选。外部表适合按身份证号、姓名、车牌号、证件号、案件编号进行定位。例如:
cert_info、entry_rec、criminal、fugitive。vehicle_info、veh_violation、co_veh_viol。fugitive.caseNo、fugi_co.caseNo、fugi_co_revoke.caseNo。当前外部表字段大多定义为 VARCHAR,且 table_field.field_type 统一为 TEXT。因此:
常见关联路径:
personName 或 personCertNo 在通话、交易、出行、住宿、外部人员表之间交叉查找。companionName、companionName 在 together_flight_info、together_train_info 中查找同行人员。通过 togetherCertNo 在 together_live_info 中查找同住人员。
sequenceDiagram
participant User as "调查员"
participant File as "file_info"
participant Internal as "案件内部表"
participant External as "外部表"
User->>File: "按案件 ID 查询文件"
File-->>User: "返回 fileId 列表"
User->>Internal: "按 fileId 查询通话、交易、出行、住宿"
Internal-->>User: "返回案件内部数据"
User->>External: "按 fileId 查询外部扩展实体"
External-->>User: "返回出入境、车辆、在逃等数据"
图表来源
章节来源
当前仓库未使用 Flyway 或 Liquibase。user.sql 注释明确说明:“执行方式:手工执行(项目无 Flyway / Liquibase)”。因此:
CREATE TABLE IF NOT EXISTS、ADD COLUMN IF NOT EXISTS、CREATE INDEX IF NOT EXISTS 等幂等操作。user.sql 负责 sys_user、初始管理员、case_info.owner_id 字段和索引。case_table.sql、case_table_1.sql、external_tables.sql、external_table_field.sql 负责案件数据结构和外部表元数据。file_ai_profile 在建表注释中说明会通过代码中的 CaseDataSourceRegistry#ensureAiProfileTable 做幂等补建,并通过 AI_PROFILE_MIGRATIONS 补列;修改列时必须同步代码常量与测试。当前 SQL 脚本普遍使用 DROP TABLE IF EXISTS 重建表,这更适合初始化或测试环境,不适合生产直接运行。生产删除策略建议:
DROP TABLE。deleted、delete_time 或类似字段。table_data_1.sql 提供了一条 govern_conf 配置样例,用于控制是否使用“开卡信息”等能力。治理类开关应放在 govern_conf 或专用配置表中,不应散落在业务表。
章节来源
user.sql 明确说明:
is_admin 一个标志,作用域限定在“能否调用 /user/** 和看到用户管理入口”。sys_role、sys_menu、sys_permission、sys_user_role。StpUtil.hasRole、checkPermission 一律不用。permCodeList 保持空。这意味着:数据库层面没有细粒度行级权限或表级权限模型。安全边界主要由应用层和用户归属字段承担。
| 层级 | 建议 |
|---|---|
| 平台层 | 使用 sys_user.status 控制启用停用;pwd_hash 使用 BCrypt;禁止明文密码入库 |
| 案件层 | 强制按 owner_id 过滤案件查询;多租户或共享案件需在应用层实现授权校验 |
| 文件层 | 对 filePath、md5、password、charSet 等敏感字段进行脱敏或加密 |
| 业务层 | 对身份证号、手机号、车牌号、住址、照片 URL 等敏感字段进行脱敏输出 |
| 外部表层 | 外部表字段多为 VARCHAR,应避免把大体积照片或附件直接存入库;建议只存 URL 或文件 ID |
| 审计层 | 保留 clean_error_log、graph_his 等辅助表用于问题排查,但不应泄露敏感原文 |
章节来源
本案数据模型围绕“用户 → 案件 → 文件 → 内部表 / 外部表”展开:
owner_id 拥有案件。file_info 管理导入文件。table_info 和 table_field 注册,驱动字段映射、分类和排序。当前设计偏向 DuckDB 分析型查询,强调高频定位列索引和 zonemap 优化,同时缺少显式外键约束和完整权限体系。生产使用中应补充: