---
date: 2026-05-08
tags: [audit, data-quality, naming, prisma]
---

# 审计日志 entity_type 双风格共存

## 现象

`platform_audit.audit_log.entity_type` 列同时存在两种风格的值：

- **PascalCase**（Prisma 模型名）：`User`、`Document`、`Invoice`、`Payment`，由手动 `auditService.log()` 调用或 seed 数据写入
- **URL-segment**（小写复数）：`users`、`auth`、`tickets`、`regions`，由 `AuditLogInterceptor.extractEntityType()` 从 HTTP 路径自动提取

同一个用户，可能同时在两种 `entityType` 下各产生若干审计记录（例：`User`/16 条 + `users`/5 条）。

## 影响

按 `entityType` 精确匹配查询会漏数据：
- 输入 `User` 找不到 16 条 `users`
- 反之亦然

## 现修复（2026-05-08）

`audit.service.ts::getEntityHistory` 的 where 子句把 `entityType` 改为 `{ equals, mode: 'insensitive' }`：解决了大小写差异（User/USER/user 等价），但仍**不解决单复数**（`User` ≠ `users`）。

前端搜索页 placeholder 同步更新提示「大小写不敏感」，让用户知道案值。

## 根因

设计时未约定单一规范来源：
- 装饰器 `@Auditable()` 没有显式 `entityType` 参数，全靠 interceptor 从 URL 兜底
- seed 与手动 `log()` 走另一条路径，按 Prisma 模型名

## 后续建议（不在本次范围）

1. **统一为 PascalCase 模型名**（推荐）：interceptor 加一份 url-segment → 模型名映射（`users → User`），写入时归一
2. **运行一次性 ETL** 把历史的 `users` 改写为 `User`（注意：审计表禁止 UPDATE，需走分区切换或追加修正记录）
3. **Code Review checklist** 加一条：用 `@Auditable({ entityType: 'User' })` 显式指定，禁止依赖 URL 自动推断
