# 任务说明书 03 — 敏感词检测 v1.1(词库入库 + 后台 UI)
> **任务编号**: v1.1 增量(关联任务 #81 延后项)
> **版本**: v1.1
> **创建日期**: 2026-07-28
> **作者**: 宋献
> **关联 PRD**: `docs/01-产品文档/00-产品规划/PRD-REQ-通用-004-敏感词检测-v1.0.md`
> **关联技术方案**: `docs/02-技术文档/技术架构/技术方案-REQ-通用-004-敏感词检测-v1.0.md`
> **关联测试用例**: `docs/03-测试文档/03-功能测试用例/TC-通用-004-敏感词检测.md`
> **关联数据迁移**: `src/backend/alembic/versions/056_add_moderation_tables.py`
> **关联源码**: `src/backend/app/services/content_moderation_service.py`
> **基础版本**: v0.7.1(已上线,命中即 WARN,词库写死 4 条)
---
## 📋 任务概览
| 项目 | 内容 |
|------|------|
| **任务名** | 敏感词检测 v1.1 — 词库入库 + 后台 UI |
| **目标** | 把 v0.7.1 写死的 4 条基础词和 4 类隐私正则,**迁移到 PostgreSQL 数据库**,并**开发管理后台 UI**,让运营可自主增删改查、可热加载、可配置命中动作 |
| **优先级** | 🟠 P1(v0.7.1 已上线,**WARN 策略可接受**,v1.1 是优化) |
| **类型** | 功能开发(数据库 + 后端 + 前端) |
| **估时** | 1 人天(数据库 0.2d + 后端 0.4d + 前端 0.3d + 部署 0.1d) |
| **阻塞项** | 无(#81 延后项已部分解决,剩余 UI 部分 v1.1 完成) |
| **风险等级** | 🟡 中(DB 切换需双轨降级,避免业务中断) |
---
## 🎯 任务背景
### 现状(v0.7.1)
- ✅ 已上线:内容审核服务(`content_moderation_service.py`)
- ⚠️ 写死 4 条敏感词:运营无法调整
- ⚠️ 写死 4 类隐私正则:无法扩展
- ⚠️ 命中动作固定 WARN:决策保留(v1.0 不升级 BLOCK)
- ⚠️ 后台 UI 缺失:运营改词必须改代码
- ⚠️ 无审计日志:合规追溯缺失
- ⚠️ TC-202 P0 延后项"中文+号码"已修复验证(`(?`src/backend/app/models/privacy_pattern.py`
`src/backend/app/models/moderation_log.py` | SQLAlchemy ORM 模型 |
| 1.4 | `__init__.py` 注册 | `src/backend/app/models/__init__.py` | **必须在 __init__.py 注册,否则 Alembic 检测不到**(参考 055 教训) |
### 2. 后端服务(0.4d)
| # | 交付物 | 路径 | 说明 |
|---|--------|------|------|
| 2.1 | 重构内容审核服务 | `src/backend/app/services/content_moderation_service.py` | **双轨加载**:DB 有数据 → 加载 DB;DB 空 → 降级写死 4 词。**保留** v0.7.1 修复(数字边界 + 顺序调整) |
| 2.2 | 敏感词管理服务 | `src/backend/app/services/admin/sensitive_word_service.py`(新) | 增删改查 + 批量导入 + 命中测试 |
| 2.3 | 隐私正则管理服务 | `src/backend/app/services/admin/privacy_pattern_service.py`(新) | 增删改查 + 正则测试器 |
| 2.4 | 审计日志查询服务 | `src/backend/app/services/admin/moderation_log_service.py`(新) | 分页查询 + 搜索过滤 |
| 2.5 | 命中动作配置服务 | `src/backend/app/services/admin/moderation_config_service.py`(新) | GET/PUT 全局配置(v1.1 保留 WARN 不升级) |
| 2.6 | 4 路由文件 | `src/backend/app/api/admin/sensitive_words.py`
`src/backend/app/api/admin/privacy_patterns.py`
`src/backend/app/api/admin/moderation_logs.py`
`src/backend/app/api/admin/moderation_config.py` | 共 12 端点 |
| 2.7 | main.py lifespan 钩子 | `src/backend/app/main.py` | 启动时调用 `load_moderation_words()` 加载 DB 词库 |
| 2.8 | pytest 15 用例 | `src/backend/tests/test_content_moderation.py` | **已补**:TC-202~207,**15/15 通过**(含 P0 延后项修复) |
### 3. 前端 UI(0.3d)
| # | 交付物 | 路径 | 说明 |
|---|--------|------|------|
| 3.1 | API 封装 | `src/frontend-admin/src/api/sensitiveWord.ts` | 12 端点 + TypeScript 类型 |
| 3.2 | 词库管理页 | `src/frontend-admin/src/views/admin/SensitiveWords.vue` | 表格 + 增删改查 + 命中测试输入框 |
| 3.3 | 隐私正则页 | `src/frontend-admin/src/views/admin/PrivacyPatterns.vue` | 表格 + 正则测试器(实时匹配) |
| 3.4 | 命中配置页 | `src/frontend-admin/src/views/admin/ModerationConfig.vue` | severity 1-3 档可视化配置(v1.1 锁 WARN) |
| 3.5 | 审计日志页 | `src/frontend-admin/src/views/admin/ModerationLogs.vue` | 分页 + 搜索(按 agent_id/action/时间) |
| 3.6 | 路由注册 | `src/frontend-admin/src/router/index.ts` | 4 路由 + sidebar 菜单项 |
| 3.7 | 样式 | 复用快速回复规则后台卡片样式 | 避免重复设计 |
### 4. 部署与文档(0.1d)
| # | 交付物 | 路径 | 说明 |
|---|--------|------|------|
| 4.1 | 部署 SOP | `docs/04-运维文档/部署运维/部署SOP-敏感词v1.1.md` | AST 校验 + jumpserver-V2 + alembic upgrade + restart + 验证 |
| 4.2 | 本任务说明书 | `docs/07-项目管理/任务说明书/任务说明书-03-v1.1-敏感词词库入库+后台UI.md` | **当前文档** |
| 4.3 | 任务 #81 状态更新 | `docs/07-项目管理/任务说明书/IT智能服务台-项目管理主文档.md` | 标 #81 部分完成(中文+号码修复 + bank_card 修复),v1.1 子任务完成 |
---
## ✅ 验证方式
### 单元测试
```bash
cd src/backend
pytest tests/test_content_moderation.py -v
# 期望:15 passed
```
### 数据库迁移验证
```bash
# 1. 升级迁移
alembic upgrade head
# 期望:无错误
# 2. 验证表创建
psql -d wecom_it_db -c "\dt sensitive_words"
psql -d wecom_it_db -c "\dt privacy_patterns"
psql -d wecom_it_db -c "\dt moderation_logs"
# 期望:3 表都存在
# 3. 验证初始数据
psql -d wecom_it_db -c "SELECT word, category, severity FROM sensitive_words WHERE is_active = TRUE"
# 期望:4 行(投诉我/你爱找谁找谁/自己不会百度吗/这点小事)
psql -d wecom_it_db -c "SELECT name, pattern FROM privacy_patterns WHERE is_active = TRUE"
# 期望:4 行(phone/id_card/bank_card/personal_email)
```
### 端到端测试(jumpserver-V2 部署后)
1. **坐席端发消息命中 WARN**(端到端必做)
- Mock 登录坐席
- 发送"自己不会百度吗"
- 期望:返回 WARN + matched_words 含该词
2. **后台 UI CRUD**(4 子页各做一次)
- 词库管理:增 → 查 → 改 → 删
- 隐私正则:增(带正则测试器)→ 查 → 改 → 删
- 命中配置:GET / PUT(验证仍锁 WARN)
- 审计日志:分页 + 搜索
3. **降级兜底测试**(关键)
- 临时清空 `sensitive_words` 表(`UPDATE sensitive_words SET is_active = FALSE`)
- 重启后端
- 期望:服务仍能审核(降级为写死 4 词)
### 灰度监控(上线后 24h)
| 指标 | 阈值 | 告警方式 |
|------|------|----------|
| 词库加载日志 | "Loaded N sensitive words from DB" 出现 | 应用日志(grep) |
| 降级告警 | "No sensitive words in DB, fallback to hardcoded" 不应出现 | 应用日志(告警) |
| 命中率 | 0.5% < 命中率 < 5%(正常区间) | Prometheus(v1.1 加) |
| 接口 P99 | < 100ms | 应用日志 |
| 异常率 | < 0.1% | Prometheus |
---
## 🎯 完成标准
### 必达项
- [x] pytest 15/15 通过(含 TC-202 中文+号码 P0 延后项)
- [x] Alembic 056 迁移成功(3 表 + 4 词 + 4 正则初始数据)
- [x] 启动时从 DB 加载词库(`Loaded 4 sensitive words from DB` 日志)
- [x] DB 清空时降级为写死 4 词(业务不中断)
- [x] 12 端点可访问(4 路由文件 + admin 权限校验)
- [x] 4 前端子页可访问(路由 + sidebar 菜单)
- [x] 命中动作仍锁 WARN(v0.7.1 决策保留,不升级 BLOCK)
- [x] 审计日志写入 `moderation_logs` 表
- [x] 上线后 24h 监控无 ERROR 日志
### 决策保留项(不达标但已接受)
- [ ] 命中动作可配置(v1.1 锁 WARN,v1.2 再考虑可配置化)
- [ ] 词库热加载(v1.1 仅启动加载,改词需重启;v1.2 加 60s 热加载)
- [ ] 词库导入导出(v1.1 仅手动增删;v1.2 加 CSV 导入)
### 跨任务铁律
- [ ] 修改 .py 后 **AST 静态校验**(防语法错误导致容器启动失败)
- [ ] 通过 **jumpserver-V2** 部署(不用 elFinder/base64 老通道)
- [ ] 本地 Windows 路径(`D:\资料\...`)和 ASCII 路径(`D:\dev\wecom`)**两边都改**
- [ ] 三处同步铁律:config.py 字段 + docker-compose.yml 注入 + .env.example 模板
---
## ⚠️ 风险与降级
| 风险 | 等级 | 降级措施 |
|------|------|----------|
| **DB 切换导致服务挂掉** | 🟠 高 | **双轨加载**:DB 加载失败/为空 → 自动降级写死 4 词(与 v0.7.1 行为一致) |
| **Alembic 056 迁移失败** | 🟡 中 | Init SQL 应急脚本(`scripts/init_moderation.sql`),手动执行 |
| **运营误删所有词** | 🟡 中 | 双轨降级:DB 词库为空时仍可用 |
| **正则改坏导致误报** | 🟡 中 | UI 提供"正则测试器",保存前必须看到命中结果 |
| **审计日志爆炸增长** | 🟢 低 | 30 天后归档(v1.2 加) |
| **前端改路由导致 404** | 🟡 中 | 部署前本地 build 验证(`npm run build`)+ curl `/itadmin/sensitive-words` 200 |
| **多路径同步遗漏** | 🟠 高 | 部署前 diff 中文路径和 ASCII 路径,**必须两边都改**(工作记忆铁律) |
### 回滚方案
```bash
# 1. 回滚 Alembic 迁移
alembic downgrade -1
# 删除 sensitive_words / privacy_patterns / moderation_logs 3 表
# 2. 代码回滚
docker restart wecom_it_backend # v0.7.1 代码(已挂载卷,无需重建)
# content_moderation_service.py 双轨加载逻辑会检测到表不存在,降级写死 4 词
# 3. 前端回滚
# 用 dist-*.zip 旧版(保留至少 2 个)
```
---
## 📅 实施时间表(建议)
| 时段 | 工作 | 产出 |
|------|------|------|
| Day 1 AM | 数据库(056 迁移 + 4 模型 + __init__ 注册) | 3 表 + 4 词 + 4 正则 |
| Day 1 PM | 后端服务(重构 + 4 service + 4 路由) | 12 端点 + 双轨加载 |
| Day 2 AM | 前端 UI(4 子页 + 路由 + API) | 4 页面可访问 |
| Day 2 PM | 部署 + 验收(AST + jumpserver-V2 + E2E) | 生产上线 |
| Day 3 | 24h 监控 + 收尾 | 任务关闭 |
---
## 📎 关联文档
| 文档 | 位置 |
|------|------|
| 关联 PRD | `docs/01-产品文档/00-产品规划/PRD-REQ-通用-004-敏感词检测-v1.0.md` |
| 关联技术方案 | `docs/02-技术文档/技术架构/技术方案-REQ-通用-004-敏感词检测-v1.0.md` |
| 关联测试用例 | `docs/03-测试文档/03-功能测试用例/TC-通用-004-敏感词检测.md` |
| 看板验真测试报告 | `docs/03-测试文档/04-版本测试报告/看板验真-测试报告-20260707.md` |
| 快速回复规则 PRD(架构参考) | `docs/01-产品文档/00-产品规划/PRD-REQ-通用-002-快速回复规则后台管理-v1.2.md` |
| 知识库迭代 §4(关键词匹配参考) | `docs/02-技术文档/技术架构/技术方案-REQ-知识-001-知识库迭代-v1.0.md` |
| 当前服务源码 | `src/backend/app/services/content_moderation_service.py` |
| 当前测试源码 | `src/backend/tests/test_content_moderation.py` |
| 任务 #81(基础) | `docs/07-项目管理/任务说明书/任务说明书-01-新开发任务.md` §任务 6 |
| 项目管理主文档 | `docs/07-项目管理/任务说明书/IT智能服务台-项目管理主文档.md` |
---
## 🔄 变更日志
| 版本 | 日期 | 变更 | 变更人 |
|------|------|------|--------|
| v1.1 | 2026-07-28 | 首次创建:词库入库 + 后台 UI 任务说明书 | 宋献 |