Files
leaudit-platform-backend/docs/系统使用统计/系统使用统计表设计.md
T
2026-05-09 20:04:08 +08:00

613 lines
14 KiB
Markdown

# 系统使用统计表设计
## 1. 设计目标
基于《系统使用统计最终需求》和《系统使用统计接口设计》,设计一套可落地的数据库方案,用于支持以下统计场景:
- 登录统计
- 文档上传统计
- 文档评查统计
- 按用户、部门、地区、文档大类、文档类型、入口模块进行动态汇总
- 支持时间范围查询、排行榜、趋势图与明细导出
本次设计遵循两个原则:
- 能复用现有业务表的地方尽量复用
- 只为现有表无法满足的统计能力补最小新增表结构
## 2. 总体设计思路
统计数据来源分为两类:
- 现有业务事实表
- 新增统计事件表
其中:
- 上传、评查相关统计,尽量直接复用现有业务表
- 登录统计由于当前没有稳定入库明细,需要新增登录事件表
- 为了提升用户管理页的查询效率,建议给 `sso_users` 增加最近登录时间字段
## 3. 现有可复用表
### 3.1 用户表 `sso_users`
用途:
- 用户基础信息来源
- 部门、地区、组织维度来源
- 最近登录时间展示
当前已使用字段:
- `id`
- `sub`
- `username`
- `nick_name`
- `phone_number`
- `email`
- `ou_id`
- `ou_name`
- `area`
- `tenant_name`
- `dep_name`
- `dep_short_name`
- `status`
- `deleted_at`
代码参考:
- `fastapi_modules/fastapi_leaudit/services/impl/authServiceImpl.py:36`
- `fastapi_modules/fastapi_leaudit/services/impl/rbacAdminServiceImpl.py:374`
### 3.2 文档主表 `leaudit_documents`
用途:
- 文档主记录
- 文档类型、地区、版本、处理状态来源
主要字段:
- `id`
- `type_id`
- `group_id`
- `region`
- `processing_status`
- `current_run_id`
- `version_group_key`
- `version_no`
- `is_latest_version`
- `created_at`
- `updated_at`
- `deleted_at`
代码参考:
- `fastapi_modules/fastapi_leaudit/models/leauditDocument.py:19`
### 3.3 文档文件表 `leaudit_document_files`
用途:
- 上传主文件统计
- 上传附件统计
- 上传人、上传时间明细来源
主要字段:
- `id`
- `document_id`
- `file_role`
- `file_name`
- `file_ext`
- `mime_type`
- `file_size`
- `oss_url`
- `is_active`
- `created_by`
- `created_at`
口径建议:
- 主文件上传:`file_role = 'primary'`
- 附件上传:`file_role = 'attachment'`
代码参考:
- `fastapi_modules/fastapi_leaudit/models/leauditDocumentFile.py:15`
- `fastapi_modules/fastapi_leaudit/services/impl/documentServiceImpl.py:224`
- `fastapi_modules/fastapi_leaudit/services/impl/documentServiceImpl.py:889`
### 3.4 评查运行表 `leaudit_audit_runs`
用途:
- 评查发起次数
- 评查完成次数
- 评查失败次数
- 评查时间趋势
- 评查状态与结果明细
主要字段:
- `id`
- `document_id`
- `document_file_id`
- `run_no`
- `trigger_source`
- `trigger_user_id`
- `status`
- `phase`
- `rule_set_id`
- `rule_version_id`
- `result_status`
- `total_score`
- `passed_count`
- `failed_count`
- `skipped_count`
- `started_at`
- `finished_at`
- `created_at`
- `updated_at`
现状说明:
- 表结构已预留 `trigger_user_id`
- 但当前代码创建 run 时尚未写入该字段
- 统计功能上线前建议补齐该字段写入逻辑
代码参考:
- `fastapi_modules/fastapi_leaudit/models/leauditAuditRun.py:16`
- `fastapi_modules/fastapi_leaudit/services/impl/auditServiceImpl.py:203`
### 3.5 评查指标表 `leaudit_run_metrics`
用途:
- 评查页数
- 评查耗时
- 规则数量等运行指标
主要字段:
- `run_id`
- `ocr_seconds`
- `normalize_seconds`
- `extract_seconds`
- `evaluate_seconds`
- `rescue_seconds`
- `total_seconds`
- `page_count`
- `sub_document_count`
- `field_count`
- `rule_count`
- `llm_call_count`
- `vlm_call_count`
- `rescue_rule_count`
- `artifact_count`
代码参考:
- `fastapi_modules/fastapi_leaudit/leaudit_bridge/storage_adapter.py:260`
### 3.6 文档类型表 `leaudit_document_types`
用途:
- 文档类型维度统计
- 入口模块归属
建议依赖字段:
- `id`
- `code`
- `name`
- `entry_module_id`
- `is_enabled`
- `sort_order`
### 3.7 入口模块表 `leaudit_entry_modules`
用途:
- 入口模块维度统计
- 首页/业务模块使用分析
建议依赖字段:
- `id`
- `name`
- `path`
- `sort_order`
- `is_enabled`
## 4. 建议调整的现有表
### 4.1 为 `sso_users` 增加最近登录时间字段
#### 建议新增字段
```sql
ALTER TABLE sso_users
ADD COLUMN IF NOT EXISTS last_login_at TIMESTAMPTZ NULL;
```
#### 字段说明
| 字段 | 类型 | 允许空 | 说明 |
| --- | --- | --- | --- |
| `last_login_at` | `TIMESTAMPTZ` | 是 | 最近一次登录成功时间 |
#### 用途
- 用户列表展示“最近登录时间”
- 后台快速判断用户活跃情况
- 避免每次都扫登录事件表取最大时间
#### 更新规则
- 登录成功:更新 `last_login_at`
- 登录失败:不更新
## 5. 新增表设计
### 5.1 登录事件表 `usage_login_events`
#### 设计目的
当前系统缺少结构化登录明细表,无法稳定支撑:
- 登录次数
- 登录用户数
- 按部门登录统计
- 按地区登录统计
- 登录趋势图
- 最近登录明细导出
因此建议新增登录事件表。
#### 建表建议
```sql
CREATE TABLE IF NOT EXISTS usage_login_events (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NULL,
sub VARCHAR(128) NULL,
username_snapshot VARCHAR(128) NULL,
nick_name_snapshot VARCHAR(128) NULL,
department_name_snapshot VARCHAR(255) NULL,
ou_id_snapshot VARCHAR(128) NULL,
ou_name_snapshot VARCHAR(255) NULL,
area_snapshot VARCHAR(64) NULL,
login_time TIMESTAMPTZ NOT NULL DEFAULT NOW(),
login_result VARCHAR(16) NOT NULL,
login_type VARCHAR(32) NOT NULL,
ip_address VARCHAR(64) NULL,
user_agent VARCHAR(1024) NULL,
client_type VARCHAR(32) NULL,
token_jti VARCHAR(128) NULL,
failure_reason VARCHAR(255) NULL,
extra JSONB NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
```
#### 字段说明
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `id` | `BIGSERIAL` | 主键 |
| `user_id` | `BIGINT` | 用户 ID,失败登录时可为空 |
| `sub` | `VARCHAR(128)` | 登录标识,兼容账号字段 |
| `username_snapshot` | `VARCHAR(128)` | 登录时用户名快照 |
| `nick_name_snapshot` | `VARCHAR(128)` | 登录时姓名快照 |
| `department_name_snapshot` | `VARCHAR(255)` | 登录时部门快照 |
| `ou_id_snapshot` | `VARCHAR(128)` | 登录时组织 ID 快照 |
| `ou_name_snapshot` | `VARCHAR(255)` | 登录时组织名称快照 |
| `area_snapshot` | `VARCHAR(64)` | 登录时地区快照 |
| `login_time` | `TIMESTAMPTZ` | 登录时间 |
| `login_result` | `VARCHAR(16)` | `success` / `failed` |
| `login_type` | `VARCHAR(32)` | `password` / `oauth` |
| `ip_address` | `VARCHAR(64)` | 登录来源 IP |
| `user_agent` | `VARCHAR(1024)` | 浏览器/终端标识 |
| `client_type` | `VARCHAR(32)` | `pc` / `mobile` / `other` |
| `token_jti` | `VARCHAR(128)` | 成功登录后的 token jti,可选 |
| `failure_reason` | `VARCHAR(255)` | 失败原因 |
| `extra` | `JSONB` | 扩展字段 |
| `created_at` | `TIMESTAMPTZ` | 记录创建时间 |
#### 索引建议
```sql
CREATE INDEX IF NOT EXISTS idx_usage_login_events_login_time
ON usage_login_events(login_time DESC);
CREATE INDEX IF NOT EXISTS idx_usage_login_events_user_id
ON usage_login_events(user_id, login_time DESC);
CREATE INDEX IF NOT EXISTS idx_usage_login_events_department
ON usage_login_events(department_name_snapshot, login_time DESC);
CREATE INDEX IF NOT EXISTS idx_usage_login_events_area
ON usage_login_events(area_snapshot, login_time DESC);
CREATE INDEX IF NOT EXISTS idx_usage_login_events_result
ON usage_login_events(login_result, login_time DESC);
```
#### 统计口径建议
- 登录次数:仅统计 `login_result = 'success'`
- 登录失败次数:统计 `login_result = 'failed'`
- 最近登录时间:按用户取最后一次成功登录时间
### 5.2 文档大类映射表 `usage_document_category_mappings`(可选但强烈建议)
#### 设计目的
需求中明确要求:
- 不写死“合同类”“案卷类”
- 后续新增文档大类、文档类型后可自动纳入统计
如果当前系统已经有稳定的一级文档大类实体,可直接复用;
如果没有一张适合统计使用的稳定“大类表”,建议新增一张统计映射表。
#### 建表建议
```sql
CREATE TABLE IF NOT EXISTS usage_document_category_mappings (
id BIGSERIAL PRIMARY KEY,
category_code VARCHAR(64) NOT NULL,
category_name VARCHAR(128) NOT NULL,
document_type_id BIGINT NOT NULL,
entry_module_id BIGINT NULL,
is_enabled BOOLEAN NOT NULL DEFAULT TRUE,
sort_order INTEGER NOT NULL DEFAULT 0,
created_by BIGINT NULL,
updated_by BIGINT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMPTZ NULL
);
```
#### 字段说明
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `id` | `BIGSERIAL` | 主键 |
| `category_code` | `VARCHAR(64)` | 统计大类编码 |
| `category_name` | `VARCHAR(128)` | 统计大类名称 |
| `document_type_id` | `BIGINT` | 关联文档类型 ID |
| `entry_module_id` | `BIGINT` | 可选,关联入口模块 |
| `is_enabled` | `BOOLEAN` | 是否启用 |
| `sort_order` | `INTEGER` | 排序 |
| `created_by` | `BIGINT` | 创建人 |
| `updated_by` | `BIGINT` | 更新人 |
| `created_at` | `TIMESTAMPTZ` | 创建时间 |
| `updated_at` | `TIMESTAMPTZ` | 更新时间 |
| `deleted_at` | `TIMESTAMPTZ` | 软删除时间 |
#### 约束与索引建议
```sql
CREATE UNIQUE INDEX IF NOT EXISTS uq_usage_document_category_type
ON usage_document_category_mappings(document_type_id)
WHERE deleted_at IS NULL;
CREATE INDEX IF NOT EXISTS idx_usage_document_category_code
ON usage_document_category_mappings(category_code, is_enabled);
CREATE INDEX IF NOT EXISTS idx_usage_document_category_module
ON usage_document_category_mappings(entry_module_id, is_enabled);
```
#### 说明
- 一种文档类型只归属一个统计大类
- 若后续系统已有成熟的一级文档大类表,可取消本表,直接复用业务表
## 6. 查询口径与表关系建议
### 6.1 登录统计
来源:
- `usage_login_events`
- `sso_users.last_login_at`
用途:
- 登录次数
- 登录用户数
- 最近登录时间
- 按部门、地区统计登录情况
### 6.2 上传统计
来源:
- `leaudit_document_files`
- `leaudit_documents`
- `sso_users`
- `leaudit_document_types`
- `leaudit_entry_modules`
- `usage_document_category_mappings`(若启用)
口径:
- 主文件上传:`leaudit_document_files.file_role = 'primary'`
- 附件上传:`leaudit_document_files.file_role = 'attachment'`
### 6.3 评查统计
来源:
- `leaudit_audit_runs`
- `leaudit_documents`
- `leaudit_document_types`
- `leaudit_entry_modules`
- `usage_document_category_mappings`(若启用)
口径:
- 发起评查次数:`leaudit_audit_runs` 记录数
- 完成评查次数:`status = 'completed'`
- 失败评查次数:`status = 'failed'`
### 6.4 地区统计
支持两种口径:
- 用户地区口径
- 登录:取 `usage_login_events.area_snapshot`
- 上传:取上传人 `sso_users.area`
- 评查:优先取触发人地区
- 文档地区口径
- 上传:取 `leaudit_documents.region`
- 评查:取 `leaudit_documents.region`
## 7. 一期是否需要聚合表
### 7.1 一期建议
一期先不新增日汇总聚合表,先使用:
- 明细事实表 + SQL 聚合
原因:
- 功能验证阶段,需求还可能变
- 统计范围目前只有登录、上传、评查三类,复杂度可控
- 先把口径跑通,比过早做聚合更重要
### 7.2 二期建议
若后续数据量变大,再补以下日汇总表:
- `usage_user_daily_stats`
- `usage_department_daily_stats`
- `usage_area_daily_stats`
- `usage_document_category_daily_stats`
- `usage_entry_module_daily_stats`
## 8. 配套代码改造建议
### 8.1 登录成功时
需要做两件事:
- 写入 `usage_login_events`
- 更新 `sso_users.last_login_at`
### 8.2 登录失败时
建议:
- 写入 `usage_login_events`
- 不更新 `sso_users.last_login_at`
### 8.3 触发评查时
建议补齐:
- `leaudit_audit_runs.trigger_user_id`
当前表已存在该字段,但创建 run 时未写入,需要补逻辑。
## 9. 推荐 SQL 变更清单
### 9.1 为 `sso_users` 增加最近登录时间
```sql
ALTER TABLE sso_users
ADD COLUMN IF NOT EXISTS last_login_at TIMESTAMPTZ NULL;
```
### 9.2 新增登录事件表
```sql
CREATE TABLE IF NOT EXISTS usage_login_events (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NULL,
sub VARCHAR(128) NULL,
username_snapshot VARCHAR(128) NULL,
nick_name_snapshot VARCHAR(128) NULL,
department_name_snapshot VARCHAR(255) NULL,
ou_id_snapshot VARCHAR(128) NULL,
ou_name_snapshot VARCHAR(255) NULL,
area_snapshot VARCHAR(64) NULL,
login_time TIMESTAMPTZ NOT NULL DEFAULT NOW(),
login_result VARCHAR(16) NOT NULL,
login_type VARCHAR(32) NOT NULL,
ip_address VARCHAR(64) NULL,
user_agent VARCHAR(1024) NULL,
client_type VARCHAR(32) NULL,
token_jti VARCHAR(128) NULL,
failure_reason VARCHAR(255) NULL,
extra JSONB NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
```
### 9.3 新增登录事件索引
```sql
CREATE INDEX IF NOT EXISTS idx_usage_login_events_login_time
ON usage_login_events(login_time DESC);
CREATE INDEX IF NOT EXISTS idx_usage_login_events_user_id
ON usage_login_events(user_id, login_time DESC);
CREATE INDEX IF NOT EXISTS idx_usage_login_events_department
ON usage_login_events(department_name_snapshot, login_time DESC);
CREATE INDEX IF NOT EXISTS idx_usage_login_events_area
ON usage_login_events(area_snapshot, login_time DESC);
CREATE INDEX IF NOT EXISTS idx_usage_login_events_result
ON usage_login_events(login_result, login_time DESC);
```
### 9.4 新增统计大类映射表(可选)
```sql
CREATE TABLE IF NOT EXISTS usage_document_category_mappings (
id BIGSERIAL PRIMARY KEY,
category_code VARCHAR(64) NOT NULL,
category_name VARCHAR(128) NOT NULL,
document_type_id BIGINT NOT NULL,
entry_module_id BIGINT NULL,
is_enabled BOOLEAN NOT NULL DEFAULT TRUE,
sort_order INTEGER NOT NULL DEFAULT 0,
created_by BIGINT NULL,
updated_by BIGINT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMPTZ NULL
);
```
## 10. 一期最终建议
一期最小可落地表设计建议如下:
必须做:
- `sso_users.last_login_at`
- `usage_login_events`
- 补齐 `leaudit_audit_runs.trigger_user_id` 写入逻辑
建议做:
- `usage_document_category_mappings`
可后置:
- 各类日汇总聚合表
- 复杂会话表
- 全量行为审计表