1 Commits

Author SHA1 Message Date
talesofzes fcbd728457 文档:完善泽源后端协作基线 2026-06-22 16:27:57 +08:00
19 changed files with 296 additions and 1512 deletions
+4 -35
View File
@@ -24,9 +24,7 @@
| ----- | ------------------------------------------- | | ----- | ------------------------------------------- |
| 后端 | Python Django + Django REST Framework | | 后端 | Python Django + Django REST Framework |
| ORM | Django ORM + Django Migrations | | ORM | Django ORM + Django Migrations |
| 数据库 | PostgreSQL 18.x,作为唯一数据源;业务表、AI 对话记忆、上下文快照和日志都落同一套 PostgreSQL | | 数据库 | MySQL 8,使用平台自有应用库 |
| AI 对话记忆 | PostgreSQL 表 + `jsonb`,用于老板秘书会话、对话消息、BotContext 快照、模型请求/响应和 Agent 决策快照;AI 记忆表不能替代正式业务表 |
| Agent 编排 | 第一版不引入 LangChain / LangGraph,先用 Django service 层实现轻量 `AiSecretaryAgent` |
| 定时任务 | Django management command + 独立 scheduler 进程 | | 定时任务 | Django management command + 独立 scheduler 进程 |
| 后台管理 | Django Admin | | 后台管理 | Django Admin |
| AI 接入 | 阿里百炼 API,后端封装薄 `ai_client` | | AI 接入 | 阿里百炼 API,后端封装薄 `ai_client` |
@@ -49,68 +47,40 @@
1. `docs/contracts/数据对象约定.md` 1. `docs/contracts/数据对象约定.md`
涉及老板 AI 秘书、Agent、意图识别、上下文和草稿生成时先读:
1. `docs/specs/01_老板AI秘书与AI草稿.md`
2. `docs/contracts/数据对象约定.md`
3. `docs/contracts/API接口约定.md`
涉及 AI 对话记忆、上下文恢复、多账号会话或 PostgreSQL `jsonb` 上下文表时再读:
1. `docs/specs/01_老板AI秘书与AI草稿.md`
2. `docs/contracts/数据对象约定.md`
只处理任务、提醒、通知等业务状态时,以 PostgreSQL 正式业务表为准,不要把 AI 记忆表当业务事实源。
涉及状态流转时再读: 涉及状态流转时再读:
1. `docs/contracts/状态流转约定.md` 1. `docs/contracts/状态流转约定.md`
涉及权限、安全、日志脱敏、飞书验签、幂等或敏感信息时再读:
1. `docs/contracts/安全权限日志约定.md`
涉及飞书时再读: 涉及飞书时再读:
1. `docs/specs/03_飞书通知与反馈.md` 1. `docs/specs/03_飞书通知与反馈.md`
2. `docs/contracts/API接口约定.md` 2. `docs/contracts/API接口约定.md`
3. `docs/contracts/安全权限日志约定.md`
涉及 Review 或提交前检查时再读: 涉及 Review 或提交前检查时再读:
1. `docs/checklists/AI生成代码检查清单.md` 1. `docs/checklists/AI生成代码检查清单.md`
2. `docs/checklists/后端Review清单.md` 2. `docs/checklists/后端Review清单.md`
3. 本次新增或修改的 tests
不要每次全量读取所有 docs,避免上下文过大、重点变散。 不要每次全量读取所有 docs,避免上下文过大、重点变散。
## 事实源优先级 ## 事实源优先级
1. `docs/contracts/` 是字段、接口、状态、错误码、安全权限日志和全局约定的唯一事实源。 1. `docs/contracts/` 是字段、接口、状态、错误码和全局约定的唯一事实源。
2. `docs/specs/` 说明业务边界和模块目标,不复制完整字段表。 2. `docs/specs/` 说明业务边界和模块目标,不复制完整字段表。
3. `docs/plans/active/` 是当前施工依据,不用于偷偷改变业务边界。 3. `docs/plans/active/` 是当前施工依据,不用于偷偷改变业务边界。
4. `docs/checklists/` tests 是 Review 闸门tests 是真实约束 4. `docs/checklists/`测试是 Review 闸门。
如果 spec 和 contract 冲突,以 contract 为准,并提出文档修正建议。 如果 spec 和 contract 冲突,以 contract 为准,并提出文档修正建议。
PostgreSQL 是唯一事实源,保存用户、人员映射、AI 草稿、任务、提醒、通知、反馈、失败记录、操作日志、AI 对话消息和 BotContext 快照。
AI 对话记忆也保存在 PostgreSQL 中,第一阶段使用 `secretary_conversations``secretary_messages``bot_contexts` 等表;`bot_context_snapshot``extracted_facts`、模型请求/响应摘要等结构化弹性字段使用 `jsonb`
PostgreSQL 中的 AI 记忆表只能辅助 AI 理解、追问、恢复会话和生成草稿,不能直接作为创建任务、提醒或通知的最终依据。
## 生成代码禁止事项 ## 生成代码禁止事项
1. 不允许 AI 未经人工确认直接创建事项、提醒或发送通知。 1. 不允许 AI 未经人工确认直接创建事项、提醒或发送通知。
2. 不允许普通员工给别人创建事项或提醒。 2. 不允许普通员工给别人创建事项或提醒。
3. 不允许绕过后端权限校验,只靠前端隐藏按钮。 3. 不允许绕过后端权限校验,只靠前端隐藏按钮。
4. 不允许自动操作交易系统、公司历史数据库、后台管理系统或其他外部业务系统。 4. 不允许自动操作交易系统、公司历史数据库、后台管理系统或其他外部业务系统。
5. 不允许把 API Key、App Secret、飞书 token、OAuth code/state、回调验签密钥、一次性操作 token、完整手机号、完整邮箱写进代码或普通日志。 5. 不允许把 API Key、App Secret、飞书 token、完整手机号、完整邮箱写进代码或普通日志。
6. 不允许把老板解释中的游戏化表达写进事项标题、接收人、时间、反馈要求等结构化字段。 6. 不允许把老板解释中的游戏化表达写进事项标题、接收人、时间、反馈要求等结构化字段。
7. 不允许把第一版明确不做的功能混入主线。 7. 不允许把第一版明确不做的功能混入主线。
8. 不允许只写 AI 记忆表就认为任务、提醒或通知已经创建成功。
9. 不允许 AI 根据 PostgreSQL AI 记忆表绕过正式业务表中的权限、草稿确认和业务状态校验。
10. 不允许把老板 AI 秘书写成普通聊天接口;必须经过 `AiSecretaryAgent` 的意图识别、上下文读取、JSON 校验、草稿/追问/降级流程。
## AI 写代码推荐流程 ## AI 写代码推荐流程
@@ -122,7 +92,6 @@ PostgreSQL 中的 AI 记忆表只能辅助 AI 理解、追问、恢复会话和
-> 没有 plan:读必要 contracts,生成 plan 给人确认 -> 没有 plan:读必要 contracts,生成 plan 给人确认
-> 已有 plan:只按当前 plan 执行 -> 已有 plan:只按当前 plan 执行
-> AI 读 checklists -> AI 读 checklists
-> AI 确认本次要新增或修改哪些 tests
-> AI 按 plan 写代码 -> AI 按 plan 写代码
-> AI 自测 -> AI 自测
-> 泽源跑测试 -> 泽源跑测试
+6 -8
View File
@@ -1,6 +1,6 @@
# ARCHITECTURE.md # ARCHITECTURE.md
本文是后端架构入口,说明 Django 项目内部模块边界和调用规则。具体业务需求看 `docs/specs/`,字段、接口状态、安全权限和日志边界`docs/contracts/` 为准。 本文是后端架构入口,说明 Django 项目内部模块边界和调用规则。具体业务需求看 `docs/specs/`,字段、接口状态以 `docs/contracts/` 为准。
## 架构原则 ## 架构原则
@@ -10,7 +10,6 @@
4. 所有关键动作必须写操作日志或失败记录。 4. 所有关键动作必须写操作日志或失败记录。
5. 飞书回调、AI 输出、定时触发必须幂等。 5. 飞书回调、AI 输出、定时触发必须幂等。
6. 权限必须在接口层和 service 层双重校验。 6. 权限必须在接口层和 service 层双重校验。
7. 敏感信息脱敏、飞书验签和访问边界以 `docs/contracts/安全权限日志约定.md` 为准。
## 运行时与依赖版本 ## 运行时与依赖版本
@@ -22,22 +21,22 @@
| Web 框架 | `Django==5.2.15` | | Web 框架 | `Django==5.2.15` |
| API 框架 | `djangorestframework==3.17.1` | | API 框架 | `djangorestframework==3.17.1` |
| ORM / Migration | Django ORM + Django Migrations,跟随 `Django==5.2.15` | | ORM / Migration | Django ORM + Django Migrations,跟随 `Django==5.2.15` |
| 数据库 | PostgreSQL `18.x`,作为唯一数据源;业务表、AI 对话记忆、上下文快照和日志都落同一套 PostgreSQL | | 数据库 | MySQL `8.4.10` LTS,使用平台自有应用库 |
| PostgreSQL 驱动 | `psycopg[binary]`,具体 patch 版本以 `requirements.txt` 锁定为准 | | MySQL 驱动 | `mysqlclient==2.2.8` |
| 定时任务 | `APScheduler==3.11.2` + `django-apscheduler==0.7.0`,通过独立 management command 进程运行 | | 定时任务 | `APScheduler==3.11.2` + `django-apscheduler==0.7.0`,通过独立 management command 进程运行 |
| AI 接入 | `dashscope==1.25.23`;百炼管理类 OpenAPI 可选 `alibabacloud-bailian20231229==2.13.1` | | AI 接入 | `dashscope==1.25.23`;百炼管理类 OpenAPI 可选 `alibabacloud-bailian20231229==2.13.1` |
| 飞书接入 | `lark-oapi==1.6.8` | | 飞书接入 | `lark-oapi==1.6.8` |
| 登录态 / 加解密 | `PyJWT==2.13.0` + `cryptography==49.0.0` | | 登录态 / 加解密 | `PyJWT==2.13.0` + `cryptography==49.0.0` |
| HTTP / 配置 | `requests==2.34.2``httpx==0.28.1``python-dotenv==1.2.2` | | HTTP / 配置 | `requests==2.34.2``httpx==0.28.1``python-dotenv==1.2.2` |
| WSGI | `gunicorn==26.0.0` | | WSGI | `gunicorn==26.0.0` |
| Docker 基础镜像 | `python:3.12.13-slim-bookworm``postgres:18``nginx:stable-alpine` | | Docker 基础镜像 | `python:3.12.13-slim-bookworm``mysql:8.4.10``nginx:stable-alpine` |
Python 依赖基线: Python 依赖基线:
```txt ```txt
Django==5.2.15 Django==5.2.15
djangorestframework==3.17.1 djangorestframework==3.17.1
psycopg[binary] mysqlclient==2.2.8
asgiref==3.11.1 asgiref==3.11.1
sqlparse==0.5.5 sqlparse==0.5.5
tzdata==2026.2 tzdata==2026.2
@@ -60,7 +59,7 @@ gunicorn==26.0.0
| --- | --- | | --- | --- |
| `accounts` | 自定义用户、角色、登录态、飞书身份绑定 | | `accounts` | 自定义用户、角色、登录态、飞书身份绑定 |
| `people` | 人员映射、称呼匹配、第一批试用人员 | | `people` | 人员映射、称呼匹配、第一批试用人员 |
| `drafts` | `AiSecretaryAgent`、AI 草稿、补充/重说、BotContext、对话消息和草稿状态流转 | | `drafts` | AI 草稿、补充/重说、草稿状态流转 |
| `tasks` | 事项创建、程经理确认、事项状态流转 | | `tasks` | 事项创建、程经理确认、事项状态流转 |
| `reminders` | 定时提醒、固定周期、触发记录 | | `reminders` | 定时提醒、固定周期、触发记录 |
| `notifications` | 飞书通知、通知补发、通知状态 | | `notifications` | 飞书通知、通知补发、通知状态 |
@@ -97,7 +96,6 @@ views
3. `notifications` 通过 `feishu` client 发送消息。 3. `notifications` 通过 `feishu` client 发送消息。
4. `feishu` 回调通过 service 更新 `drafts``feedbacks``tasks``reminders` 4. `feishu` 回调通过 service 更新 `drafts``feedbacks``tasks``reminders`
5. 所有模块写入 `audit` 5. 所有模块写入 `audit`
6. `drafts` 中的 AI 对话记忆只辅助理解和草稿修订,不替代 `tasks``reminders``notifications` 等正式业务表。
避免: 避免:
+2 -7
View File
@@ -20,8 +20,7 @@
| ----- | ------------------------------------------- | | ----- | ------------------------------------------- |
| 后端 | Python Django + Django REST Framework | | 后端 | Python Django + Django REST Framework |
| ORM | Django ORM + Django Migrations | | ORM | Django ORM + Django Migrations |
| 数据库 | PostgreSQL 18.x,作为唯一数据源;业务表、AI 对话记忆、上下文快照和日志都落同一套 PostgreSQL | | 数据库 | MySQL 8,使用平台自有应用库 |
| AI 对话记忆 | PostgreSQL 表 + `jsonb`,保存老板秘书会话、消息、BotContext 和模型调用快照 |
| 定时任务 | Django management command + 独立 scheduler 进程 | | 定时任务 | Django management command + 独立 scheduler 进程 |
| 后台管理 | Django Admin | | 后台管理 | Django Admin |
| AI 接入 | 阿里百炼 API,后端封装薄 `ai_client` | | AI 接入 | 阿里百炼 API,后端封装薄 `ai_client` |
@@ -34,7 +33,6 @@
2. Docker / 部署环境固定 Python `3.12.13`;本地开发允许 Python 3.11 或 3.12,提交前必须在 Python 3.12.13 上跑完整测试。 2. Docker / 部署环境固定 Python `3.12.13`;本地开发允许 Python 3.11 或 3.12,提交前必须在 Python 3.12.13 上跑完整测试。
3. 后端开发先采用 mock-first:单元测试和最小闭环测试不得访问真实百炼、真实飞书或公网回调。 3. 后端开发先采用 mock-first:单元测试和最小闭环测试不得访问真实百炼、真实飞书或公网回调。
4. 定时提醒 scheduler 独立于 Gunicorn worker,同一环境同一应用库同时只能运行一个 scheduler 进程。 4. 定时提醒 scheduler 独立于 Gunicorn worker,同一环境同一应用库同时只能运行一个 scheduler 进程。
5. 老板 AI 秘书统一走 `POST /api/secretary/handle-message`,AI 只生成草稿、追问或回复,不直接创建任务、提醒或通知。
## 文档入口 ## 文档入口
@@ -49,13 +47,11 @@
1. `docs/contracts/API接口约定.md` 1. `docs/contracts/API接口约定.md`
2. `docs/contracts/数据对象约定.md` 2. `docs/contracts/数据对象约定.md`
3. `docs/contracts/状态流转约定.md` 3. `docs/contracts/状态流转约定.md`
4. `docs/contracts/安全权限日志约定.md`
提交或 Review 前读: 提交或 Review 前读:
1. `docs/checklists/AI生成代码检查清单.md` 1. `docs/checklists/AI生成代码检查清单.md`
2. `docs/checklists/后端Review清单.md` 2. `docs/checklists/后端Review清单.md`
3. 本次新增或修改的 tests
## 本地启动 ## 本地启动
@@ -77,9 +73,8 @@
3. 飞书 App Secret。 3. 飞书 App Secret。
4. 飞书回调验签密钥。 4. 飞书回调验签密钥。
5. 数据库账号密码。 5. 数据库账号密码。
6. OAuth code/state 和一次性操作 token 明文。
普通日志中不得打印 API Key、App Secret、飞书 token、OAuth code/state、一次性操作 token、回调验签密钥、完整手机号或完整邮箱。 普通日志中不得打印 API Key、App Secret、飞书 token、完整手机号或完整邮箱。
## 第一版不做 ## 第一版不做
+2 -4
View File
@@ -27,12 +27,11 @@ AI 只负责整理、建议和生成草稿,不直接创建事项、不直接
## 核心模块 ## 核心模块
1. 老板 AI 秘书:一句话入口、`AiSecretaryAgent`意图分类、AI 草稿、补充/重说、普通问答兜底、对话记忆和 BotContext 1. 老板 AI 秘书:一句话入口、意图分类、AI 草稿、补充/重说、普通问答兜底。
2. 事项任务:承接确认后的事项、通知、反馈和状态查看。 2. 事项任务:承接确认后的事项、通知、反馈和状态查看。
3. 定时提醒:承接一次性和固定周期提醒,到点通知并记录结果。 3. 定时提醒:承接一次性和固定周期提醒,到点通知并记录结果。
4. 飞书集成:身份登录、老板机器人私聊、个人消息、交互卡片和回调。 4. 飞书集成:身份登录、老板机器人私聊、个人消息、交互卡片和回调。
5. 权限日志失败记录:角色权限、操作日志、失败记录、敏感信息边界。 5. 权限日志失败记录:角色权限、操作日志、失败记录、敏感信息边界。
6. 安全权限日志约定:飞书验签、幂等、日志脱敏、访问范围和 tests 真实约束。
## 第一版闭环 ## 第一版闭环
@@ -76,8 +75,7 @@ AI 解析失败 / 通知失败 / 回调失败 / 定时触发失败
| --- | --- | | --- | --- |
| 后端 | Python Django + Django REST Framework | | 后端 | Python Django + Django REST Framework |
| ORM | Django ORM + Django Migrations | | ORM | Django ORM + Django Migrations |
| 数据库 | PostgreSQL 18.x,作为唯一数据源 | | 数据库 | MySQL 8,使用平台自有应用库 |
| AI 对话记忆 | PostgreSQL 表 + `jsonb`,保存老板秘书会话、消息、BotContext 和模型调用快照 |
| 定时任务 | Django management command + 独立 scheduler 进程 | | 定时任务 | Django management command + 独立 scheduler 进程 |
| 后台管理 | Django Admin | | 后台管理 | Django Admin |
| AI 接入 | 阿里百炼 API,后端封装薄 `ai_client` | | AI 接入 | 阿里百炼 API,后端封装薄 `ai_client` |
+6 -10
View File
@@ -4,16 +4,16 @@
## 当前迭代目标 ## 当前迭代目标
按本地 `docs/specs/01_老板AI秘书与AI草稿.md` 的老板 AI 秘书方案,统一压缩包内后端协作文档、contract、active plan 和 checklist,让后续 AI 与开发者可以按同一套口径写 Django 后端代码。 补齐后端协作文档和第一份 active plan,让后续 AI 与开发者可以按同一套 spec、contract、plan 和 checklist 写 Django 后端代码。
本轮不直接实现业务代码,因为当前仓库还没有 Django 项目骨架。文档重点是先统一 AI 秘书入口、PostgreSQL 数据源、AI 对话记忆、BotContext、稳定 JSON 输出、任务/提醒/通知边界和失败记录 本轮不直接实现业务代码,因为当前仓库还没有 Django 项目骨架,且原 `docs/specs``docs/contracts``docs/checklists``docs/plans/active` 基本为空
可行性审查后,active plan 已补充 mock-first、Django 5.2 LTS、PostgreSQL 18.x、Task 1 可执行顺序、自定义用户模型首次迁移前设置、AI 对话记忆、周期提醒状态和 scheduler 单实例约束。 可行性审查后,active plan 已补充 mock-first、Django 5.2 LTS、Task 1 可执行顺序、自定义用户模型首次迁移前设置、周期提醒状态和 scheduler 单实例约束。
## 本周任务 ## 本周任务
| 事项 | 负责人 | 状态 | | 事项 | 负责人 | 状态 |
| --- | --- | --- | | ---------------------------------- | ------------- | --- |
| 补齐 `README.md` 项目说明 | 泽源 | 进行中 | | 补齐 `README.md` 项目说明 | 泽源 | 进行中 |
| 补齐 `docs/00_项目总览.md` | 王一多 / 泽源协作 | 进行中 | | 补齐 `docs/00_项目总览.md` | 王一多 / 泽源协作 | 进行中 |
| 补齐 `docs/specs/01_老板AI秘书与AI草稿.md` | 王一多 / 泽源 / 焕然 | 进行中 | | 补齐 `docs/specs/01_老板AI秘书与AI草稿.md` | 王一多 / 泽源 / 焕然 | 进行中 |
@@ -24,10 +24,8 @@
| 补齐 `docs/contracts/API接口约定.md` | 田宇 / 泽源 | 进行中 | | 补齐 `docs/contracts/API接口约定.md` | 田宇 / 泽源 | 进行中 |
| 补齐 `docs/contracts/数据对象约定.md` | 乔大卫 / 泽源 | 进行中 | | 补齐 `docs/contracts/数据对象约定.md` | 乔大卫 / 泽源 | 进行中 |
| 补齐 `docs/contracts/状态流转约定.md` | 乔大卫 | 进行中 | | 补齐 `docs/contracts/状态流转约定.md` | 乔大卫 | 进行中 |
| 新增 `docs/contracts/安全权限日志约定.md` | 乔大卫 / 田宇 / 泽源 | 进行中 |
| 补齐 `docs/checklists/AI生成代码检查清单.md` | 泽源 | 进行中 | | 补齐 `docs/checklists/AI生成代码检查清单.md` | 泽源 | 进行中 |
| 补齐 `docs/checklists/后端Review清单.md` | 乔大卫 / 田宇 | 进行中 | | 补齐 `docs/checklists/后端Review清单.md` | 乔大卫 / 田宇 | 进行中 |
| 预留并维护 `tests/` 真实约束 | 泽园 / 焕然 | 进行中 |
| 补齐第一份 active plan | 泽源 / 焕然 | 进行中 | | 补齐第一份 active plan | 泽源 / 焕然 | 进行中 |
## 当前卡点 ## 当前卡点
@@ -36,9 +34,7 @@
2. 飞书正式应用配置、回调地址、个人消息权限和第一批试用人员映射还需要联调确认。 2. 飞书正式应用配置、回调地址、个人消息权限和第一批试用人员映射还需要联调确认。
3. 阿里百炼 API Key、飞书 App Secret 等密钥不能写入仓库,需要后续通过环境变量配置。 3. 阿里百炼 API Key、飞书 App Secret 等密钥不能写入仓库,需要后续通过环境变量配置。
4. 第一批试用人员姓名、常见称呼、手机号或邮箱映射需要后续维护。 4. 第一批试用人员姓名、常见称呼、手机号或邮箱映射需要后续维护。
5. PostgreSQL 连接、迁移和 `jsonb` 字段使用方式需要在项目骨架创建后验证 5. 本地当前 Python 可能不是部署基线;提交前需要在 Python 3.12.13 环境跑完整测试
6. 本地当前 Python 可能不是部署基线;提交前需要在 Python 3.12.13 环境跑完整测试。
7. 当前仅预留 tests 目录,Django 骨架创建后需要把权限、状态、飞书幂等、调度幂等、日志脱敏和 AI 人工确认边界落成真实测试。
## 待确认问题 ## 待确认问题
@@ -51,6 +47,6 @@
## 下一步 ## 下一步
1. 完成本轮文档补齐。 1. 完成本轮文档补齐。
2. 由泽源按修订后的 active plan 创建 Django 项目骨架、基础 app、PostgreSQL 模型和迁移。 2. 由泽源按修订后的 active plan 创建 Django 项目骨架、基础 app、模型和迁移。
3. 先用 mock 百炼和 mock 飞书跑通后端最小闭环测试。 3. 先用 mock 百炼和 mock 飞书跑通后端最小闭环测试。
4. 优先跑通 AI 草稿、人工确认、事项创建、飞书通知和反馈记录这条最小链路。 4. 优先跑通 AI 草稿、人工确认、事项创建、飞书通知和反馈记录这条最小链路。
+2 -21
View File
@@ -9,10 +9,8 @@ AI 生成或修改后端代码前后都要检查本清单。checklists 是 Revie
- [ ] 已读当前 active plan。 - [ ] 已读当前 active plan。
- [ ] 涉及接口时已读 `docs/contracts/API接口约定.md` - [ ] 涉及接口时已读 `docs/contracts/API接口约定.md`
- [ ] 涉及字段或数据库时已读 `docs/contracts/数据对象约定.md` - [ ] 涉及字段或数据库时已读 `docs/contracts/数据对象约定.md`
- [ ] 涉及老板 AI 秘书、Agent、BotContext 或对话记忆时已读 `docs/specs/01_老板AI秘书与AI草稿.md`
- [ ] 涉及状态流转时已读 `docs/contracts/状态流转约定.md` - [ ] 涉及状态流转时已读 `docs/contracts/状态流转约定.md`
- [ ] 涉及飞书时已读 `docs/specs/03_飞书通知与反馈.md` - [ ] 涉及飞书时已读 `docs/specs/03_飞书通知与反馈.md`
- [ ] 涉及权限、安全、日志脱敏、飞书验签或幂等时已读 `docs/contracts/安全权限日志约定.md`
## 2. 范围控制 ## 2. 范围控制
@@ -33,13 +31,9 @@ AI 生成或修改后端代码前后都要检查本清单。checklists 是 Revie
## 4. AI 草稿 ## 4. AI 草稿
- [ ] AI 只生成草稿,不直接创建事项、提醒或通知。 - [ ] AI 只生成草稿,不直接创建事项、提醒或通知。
- [ ] 老板消息统一走 `POST /api/secretary/handle-message`
- [ ] `source + message_id` 幂等,不重复调用模型、不重复创建草稿、不重复写 `secretary_messages`
- [ ] AI 输出 JSON 已做结构校验。 - [ ] AI 输出 JSON 已做结构校验。
- [ ] `qa/realtime_qa/note/need_more_info/unknown/unsupported` 不创建事项、提醒或通知。 - [ ] `qa``realtime_qa` 不创建事项、提醒或通知。
- [ ] 实时问答第一版只做暂不支持提示,不做交易判断。 - [ ] 实时问答第一版只做暂不支持提示,不做交易判断。
- [ ] `need_more_info` 最多 3 个问题、最多 3 轮追问。
- [ ] PostgreSQL AI 对话记忆只辅助理解和草稿修订,不替代任务、提醒、通知等正式业务表。
- [ ] 结构化字段没有写入游戏化表达。 - [ ] 结构化字段没有写入游戏化表达。
- [ ] 模型调用记录了背景摘要、老板风格和业务规则版本。 - [ ] 模型调用记录了背景摘要、老板风格和业务规则版本。
- [ ] AI 解析失败会写 `model_call_logs``failure_records` - [ ] AI 解析失败会写 `model_call_logs``failure_records`
@@ -56,9 +50,8 @@ AI 生成或修改后端代码前后都要检查本清单。checklists 是 Revie
## 6. 飞书 ## 6. 飞书
- [ ] 飞书回调必须验签。 - [ ] 飞书回调必须验签。
- [ ] 飞书事件使用 `event_id``idempotency_key` 幂等。 - [ ] 飞书事件使用 `event_id` 幂等。
- [ ] 卡片重复点击不会重复创建事项、提醒或反馈。 - [ ] 卡片重复点击不会重复创建事项、提醒或反馈。
- [ ] 旧卡片、失效卡片、已替代草稿和已过期通知只记录事件,不写业务对象。
- [ ] 飞书通知只发送摘要和链接。 - [ ] 飞书通知只发送摘要和链接。
- [ ] 非老板机器人访问不会调用 AI。 - [ ] 非老板机器人访问不会调用 AI。
- [ ] 回调失败会写 `feishu_events``failure_records` - [ ] 回调失败会写 `feishu_events``failure_records`
@@ -86,8 +79,6 @@ AI 生成或修改后端代码前后都要检查本清单。checklists 是 Revie
- [ ] 普通日志不包含 API Key、App Secret、飞书 token。 - [ ] 普通日志不包含 API Key、App Secret、飞书 token。
- [ ] 普通日志不包含完整手机号或完整邮箱。 - [ ] 普通日志不包含完整手机号或完整邮箱。
- [ ] 原始错误摘要不包含密钥。 - [ ] 原始错误摘要不包含密钥。
- [ ] OAuth code/state、回调验签密钥、一次性操作 token 明文不进入日志或业务表。
- [ ] `model_call_logs` 原始请求/响应和 `feishu_events.raw_payload` 访问范围受限。
## 10. 测试 ## 10. 测试
@@ -97,13 +88,3 @@ AI 生成或修改后端代码前后都要检查本清单。checklists 是 Revie
- [ ] 定时测试覆盖重复触发防护。 - [ ] 定时测试覆盖重复触发防护。
- [ ] 事务测试覆盖通知失败不留下半截数据。 - [ ] 事务测试覆盖通知失败不留下半截数据。
- [ ] AI 测试覆盖解析失败、实时问答和人工确认边界。 - [ ] AI 测试覆盖解析失败、实时问答和人工确认边界。
- [ ] 每个 plan 完成前已说明新增或修改了哪些 tests、覆盖哪些 spec / contract 约定、如何运行。
## 11. 文档同步
- [ ] 改接口同步 `docs/contracts/API接口约定.md`
- [ ] 改字段同步 `docs/contracts/数据对象约定.md`
- [ ] 改状态同步 `docs/contracts/状态流转约定.md`
- [ ] 改权限、安全、日志脱敏、飞书验签或幂等同步 `docs/contracts/安全权限日志约定.md`
- [ ] 改飞书逻辑同步 `docs/specs/03_飞书通知与反馈.md`
- [ ] 改 AI 规则同步 `docs/specs/01_老板AI秘书与AI草稿.md`
+3 -13
View File
@@ -11,7 +11,6 @@
- [ ] 飞书身份没有直接绕过平台角色权限。 - [ ] 飞书身份没有直接绕过平台角色权限。
- [ ] 非老板机器人私聊不会触发 AI。 - [ ] 非老板机器人私聊不会触发 AI。
- [ ] 失败记录仅管理员 / AI 团队和必要管理角色可见。 - [ ] 失败记录仅管理员 / AI 团队和必要管理角色可见。
- [ ] 权限、安全、日志脱敏、验签和幂等符合 `docs/contracts/安全权限日志约定.md`
## 2. 状态流转 ## 2. 状态流转
@@ -32,9 +31,8 @@
## 4. 飞书 ## 4. 飞书
- [ ] 回调已验签。 - [ ] 回调已验签。
- [ ] `event_id``idempotency_key` 幂等。 - [ ] `event_id` 幂等。
- [ ] 卡片重复点击幂等。 - [ ] 卡片重复点击幂等。
- [ ] 旧卡片、失效卡片、已替代草稿和已过期通知不会继续写业务对象。
- [ ] 通知内容只包含摘要和链接。 - [ ] 通知内容只包含摘要和链接。
- [ ] 飞书 token、App Secret 不进入日志。 - [ ] 飞书 token、App Secret 不进入日志。
- [ ] 找不到通知、草稿、事项、提醒时有失败记录。 - [ ] 找不到通知、草稿、事项、提醒时有失败记录。
@@ -42,14 +40,10 @@
## 5. AI ## 5. AI
- [ ] AI 输出只进入草稿。 - [ ] AI 输出只进入草稿。
- [ ] 老板消息统一走 `POST /api/secretary/handle-message`
- [ ] `source + message_id` 幂等覆盖模型调用、草稿创建和 `secretary_messages`
- [ ] 模型 JSON 有 validator。 - [ ] 模型 JSON 有 validator。
- [ ] `qa` / `realtime_qa` / `note` / `need_more_info` / `unknown` / `unsupported` 不创建事项或提醒。 - [ ] `qa` / `realtime_qa` 不创建事项或提醒。
- [ ] 实时问答不做交易判断。 - [ ] 实时问答不做交易判断。
- [ ] PromptContext 版本被记录。 - [ ] prompt_contexts 版本被记录。
- [ ] PostgreSQL AI 对话记忆只辅助理解和草稿修订,不替代正式业务表。
- [ ] PostgreSQL AI 记忆读写失败会写 `memory_store_failed`,不继续依赖内存确认草稿。
- [ ] 游戏化表达没有进入结构化字段。 - [ ] 游戏化表达没有进入结构化字段。
## 6. 定时提醒 ## 6. 定时提醒
@@ -68,8 +62,6 @@
- [ ] 失败记录包含类型、关联对象、原因、处理状态。 - [ ] 失败记录包含类型、关联对象、原因、处理状态。
- [ ] 失败处理必须有处理结果。 - [ ] 失败处理必须有处理结果。
- [ ] 日志包含操作人和渠道。 - [ ] 日志包含操作人和渠道。
- [ ] OAuth code/state、回调验签密钥、一次性操作 token、完整手机号和完整邮箱没有进入普通日志。
- [ ] 模型原始请求/响应和飞书原始 payload 的访问范围受限。
## 8. 测试 ## 8. 测试
@@ -77,13 +69,11 @@
- [ ] 失败路径有测试。 - [ ] 失败路径有测试。
- [ ] 测试没有依赖真实飞书或真实百炼网络调用。 - [ ] 测试没有依赖真实飞书或真实百炼网络调用。
- [ ] 测试 fixture 不包含真实手机号、邮箱、token 或密钥。 - [ ] 测试 fixture 不包含真实手机号、邮箱、token 或密钥。
- [ ] 每个 plan 完成前说明了新增或修改的 tests、覆盖的 spec / contract 约定和运行方式。
## 9. 文档同步 ## 9. 文档同步
- [ ] 改接口同步 `docs/contracts/API接口约定.md` - [ ] 改接口同步 `docs/contracts/API接口约定.md`
- [ ] 改字段同步 `docs/contracts/数据对象约定.md` - [ ] 改字段同步 `docs/contracts/数据对象约定.md`
- [ ] 改状态同步 `docs/contracts/状态流转约定.md` - [ ] 改状态同步 `docs/contracts/状态流转约定.md`
- [ ] 改权限、安全、日志脱敏、飞书验签或幂等同步 `docs/contracts/安全权限日志约定.md`
- [ ] 改飞书逻辑同步 `docs/specs/03_飞书通知与反馈.md` - [ ] 改飞书逻辑同步 `docs/specs/03_飞书通知与反馈.md`
- [ ] 改 AI 规则同步 `docs/specs/01_老板AI秘书与AI草稿.md` - [ ] 改 AI 规则同步 `docs/specs/01_老板AI秘书与AI草稿.md`
+2 -13
View File
@@ -8,7 +8,6 @@
- [ ] 飞书登录可以匹配到平台用户和角色。 - [ ] 飞书登录可以匹配到平台用户和角色。
- [ ] 未匹配用户或停用用户不能进入平台,并有失败记录。 - [ ] 未匹配用户或停用用户不能进入平台,并有失败记录。
- [ ] 老板可以通过飞书机器人私聊提交一句话。 - [ ] 老板可以通过飞书机器人私聊提交一句话。
- [ ] 老板消息统一进入 `POST /api/secretary/handle-message`
- [ ] 非老板私聊机器人不会触发 AI,并有未授权访问记录。 - [ ] 非老板私聊机器人不会触发 AI,并有未授权访问记录。
## 2. AI 草稿 ## 2. AI 草稿
@@ -16,19 +15,15 @@
- [ ] AI 可以把老板一句话解析为结构化草稿。 - [ ] AI 可以把老板一句话解析为结构化草稿。
- [ ] AI 草稿可以展示原始输入、类型、内容、接收人候选、时间、反馈要求和缺失字段。 - [ ] AI 草稿可以展示原始输入、类型、内容、接收人候选、时间、反馈要求和缺失字段。
- [ ] 草稿必须人工确认后才创建事项或提醒。 - [ ] 草稿必须人工确认后才创建事项或提醒。
- [ ] 普通聊天、普通记录、实时问答兜底、`note``unknown` 不会创建事项、提醒或通知。
- [ ] 老板可以确认、补充/重说、取消草稿。 - [ ] 老板可以确认、补充/重说、取消草稿。
- [ ] 补充/重说 30 分钟上下文可以生效并过期。 - [ ] 补充/重说 30 分钟上下文可以生效并过期。
- [ ] 补充/重说后旧确认卡片失效,新草稿能追溯 `parent_draft_id`
- [ ] PostgreSQL 中可以查看 `secretary_messages` 和当前 `BotContext`
- [ ] 实时问答不会创建事项、提醒或通知。 - [ ] 实时问答不会创建事项、提醒或通知。
## 3. 事项闭环 ## 3. 事项闭环
- [ ] 简单事项可以从草稿生成。 - [ ] 简单事项可以从草稿生成。
- [ ] 复杂事项在老板确认后创建 `pending_manager_confirm` 事项壳 - [ ] 复杂事项可以转程经理确认
- [ ] 程经理收到飞书提醒后进入平台确认。 - [ ] 程经理收到飞书提醒后进入平台确认。
- [ ] 程经理补齐事项壳后才通知最终接收人。
- [ ] 事项可以飞书通知接收人。 - [ ] 事项可以飞书通知接收人。
- [ ] 接收人可以反馈已收到、处理中、已完成、有问题。 - [ ] 接收人可以反馈已收到、处理中、已完成、有问题。
- [ ] 有问题反馈必须留下原因。 - [ ] 有问题反馈必须留下原因。
@@ -43,7 +38,7 @@
- [ ] 自己提醒自己默认不需要反馈。 - [ ] 自己提醒自己默认不需要反馈。
- [ ] 老板 / 程经理给别人设置的提醒默认需要反馈。 - [ ] 老板 / 程经理给别人设置的提醒默认需要反馈。
- [ ] 暂停、恢复、取消按状态约定生效。 - [ ] 暂停、恢复、取消按状态约定生效。
- [ ] 同一提醒同一触发时间同一接收人同一渠道不会重复触发同一通知。 - [ ] 同一提醒不会重复触发同一通知。
- [ ] 演示 / 联调环境同一应用库只启动一个 scheduler 进程。 - [ ] 演示 / 联调环境同一应用库只启动一个 scheduler 进程。
## 5. 飞书通知和回调 ## 5. 飞书通知和回调
@@ -53,15 +48,11 @@
- [ ] 卡片按钮回调可以被平台接收。 - [ ] 卡片按钮回调可以被平台接收。
- [ ] 回调验签失败不会执行业务动作。 - [ ] 回调验签失败不会执行业务动作。
- [ ] 重复点击卡片不会重复创建反馈或转换草稿。 - [ ] 重复点击卡片不会重复创建反馈或转换草稿。
- [ ] 没有稳定 `event_id` 的回调使用 `idempotency_key` 幂等。
- [ ] 旧卡片、失效卡片、已替代草稿和已过期通知只记录事件,不写业务对象。
- [ ] 飞书手机端平台反馈页可以提交“有问题”原因,并拒绝过期或失效链接。
- [ ] 通知成功和失败都有记录。 - [ ] 通知成功和失败都有记录。
## 6. 失败复盘 ## 6. 失败复盘
- [ ] AI 解析失败有记录。 - [ ] AI 解析失败有记录。
- [ ] PostgreSQL AI 记忆读写失败有记录。
- [ ] 人员映射缺失有记录。 - [ ] 人员映射缺失有记录。
- [ ] 飞书登录失败有记录。 - [ ] 飞书登录失败有记录。
- [ ] 飞书通知失败有记录。 - [ ] 飞书通知失败有记录。
@@ -74,7 +65,5 @@
- [ ] 飞书通知只发摘要和链接。 - [ ] 飞书通知只发摘要和链接。
- [ ] 普通日志不含 API Key、App Secret、飞书 token。 - [ ] 普通日志不含 API Key、App Secret、飞书 token。
- [ ] 普通日志不含完整手机号和完整邮箱。 - [ ] 普通日志不含完整手机号和完整邮箱。
- [ ] 普通日志不含 OAuth code/state、回调验签密钥和一次性操作 token 明文。
- [ ] 模型原始请求/响应和飞书原始 payload 只对管理员 / AI 团队开放排查。
- [ ] 不自动操作交易系统、历史数据库、后台管理系统或其他外部业务系统。 - [ ] 不自动操作交易系统、历史数据库、后台管理系统或其他外部业务系统。
- [ ] 第一版明确不做的功能没有混入主线。 - [ ] 第一版明确不做的功能没有混入主线。
+10 -45
View File
@@ -11,7 +11,6 @@
5. 动作类接口使用 `POST /api/{resources}/{id}/{action}` 5. 动作类接口使用 `POST /api/{resources}/{id}/{action}`
6. 飞书回调接口必须单独记录原始 payload。 6. 飞书回调接口必须单独记录原始 payload。
7. 后端必须做权限校验,不能只靠前端隐藏入口。 7. 后端必须做权限校验,不能只靠前端隐藏入口。
8. 涉及权限、安全、日志脱敏、飞书验签和幂等时,以 `docs/contracts/安全权限日志约定.md` 为准。
## 2. 统一响应格式 ## 2. 统一响应格式
@@ -67,15 +66,11 @@
| `not_found` | 404 | 对象不存在或不可见 | | `not_found` | 404 | 对象不存在或不可见 |
| `state_conflict` | 409 | 当前状态不允许该操作 | | `state_conflict` | 409 | 当前状态不允许该操作 |
| `idempotency_conflict` | 409 | 幂等键冲突或重复请求 | | `idempotency_conflict` | 409 | 幂等键冲突或重复请求 |
| `card_expired` | 409 | 飞书卡片、通知或一次性操作链接已失效 |
| `ai_parse_failed` | 422 | AI 输出无法解析或不符合结构 | | `ai_parse_failed` | 422 | AI 输出无法解析或不符合结构 |
| `ai_model_failed` | 502 | 百炼或模型网关失败,重试后仍不可用 | | `person_mapping_missing` | 422 | 缺少人员映射 |
| `missing_person_mapping` | 422 | 缺少人员映射 |
| `memory_store_failed` | 500 | AI 对话记忆、消息或 BotContext 保存/读取失败 |
| `draft_convert_failed` | 500 | 草稿确认后转换任务或提醒失败 |
| `feishu_signature_invalid` | 401 | 飞书回调验签失败 | | `feishu_signature_invalid` | 401 | 飞书回调验签失败 |
| `feishu_api_failed` | 502 | 飞书接口调用失败 | | `feishu_api_failed` | 502 | 飞书接口调用失败 |
| `reminder_trigger_failed` | 500 | 定时提醒触发失败 | | `scheduler_trigger_failed` | 500 | 定时提醒触发失败 |
| `internal_error` | 500 | 未分类服务端错误 | | `internal_error` | 500 | 未分类服务端错误 |
## 4. 用户与人员映射 ## 4. 用户与人员映射
@@ -104,7 +99,7 @@
| 方法 | 路径 | 作用 | 权限 | | 方法 | 路径 | 作用 | 权限 |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| POST | `/api/secretary/handle-message` | 统一老板消息入口,返回草稿、追问、普通回复、上下文回顾或错误降级 | 老板;调试入口需管理员 token | | POST | `/api/ai-drafts/parse` | 提交一句话并生成草稿 | 老板;平台兜底可允许管理员测试 |
| GET | `/api/ai-drafts` | 草稿列表 | 相关用户、程经理、管理员 | | GET | `/api/ai-drafts` | 草稿列表 | 相关用户、程经理、管理员 |
| GET | `/api/ai-drafts/{id}` | 草稿详情 | 相关用户、程经理、管理员 | | GET | `/api/ai-drafts/{id}` | 草稿详情 | 相关用户、程经理、管理员 |
| PATCH | `/api/ai-drafts/{id}` | 修改草稿字段 | 草稿发起人、待确认程经理 | | PATCH | `/api/ai-drafts/{id}` | 修改草稿字段 | 草稿发起人、待确认程经理 |
@@ -113,44 +108,17 @@
| POST | `/api/ai-drafts/{id}/convert` | 转换为事项或提醒 | 草稿发起人、待确认程经理 | | POST | `/api/ai-drafts/{id}/convert` | 转换为事项或提醒 | 草稿发起人、待确认程经理 |
| POST | `/api/ai-drafts/{id}/follow-up` | 根据补充/重说重新生成草稿 | 草稿发起人 | | POST | `/api/ai-drafts/{id}/follow-up` | 根据补充/重说重新生成草稿 | 草稿发起人 |
`POST /api/secretary/handle-message` 请求示例: `POST /api/ai-drafts/parse` 请求示例:
```json ```json
{ {
"source": "feishu", "input_text": "明天上午提醒东东给我订会议室",
"message_id": "msg_xxx", "source": "platform",
"sender": { "client_context": {}
"open_id": "ou_xxx",
"name": "老板",
"role": "boss"
},
"text": "明天上午提醒东东给我订会议室",
"context": {}
} }
``` ```
响应 `data` 建议包含: 响应 `data` 必须包含 `draft_id``draft_type``status``summary``missing_fields``follow_up_questions`
- `intent`
- `reply_type`
- `answer`
- `draft`
- `questions`
- `context_summary`
- `failure`
`reply_type` 建议取值:
- `draft_preview`
- `follow_up_question`
- `plain_answer`
- `context_summary`
- `unsupported`
- `error`
`note` / `unknown` / `qa` / `realtime_qa` / `need_more_info` / `unsupported` 不得创建事项、提醒或通知;如需留痕,只返回回复、追问、上下文摘要或失败信息。
兼容接口 `/api/ai-drafts/parse``/api/demo/boss-message` 可以保留,但内部必须转发到 `/api/secretary/handle-message`
## 7. 事项 ## 7. 事项
@@ -204,11 +172,9 @@
1. 必须验签。 1. 必须验签。
2. 必须记录原始 payload 到 `feishu_events` 2. 必须记录原始 payload 到 `feishu_events`
3. 必须使用 `event_id` 幂等;没有稳定 `event_id` 时必须使用 `idempotency_key` 3. 必须使用 `event_id` 幂等。
4. 验签失败不得执行业务动作。 4. 验签失败不得执行业务动作。
5. 草稿确认类卡片必须校验 `ai_drafts.active_card_notification_id` 5. 回调处理失败必须写入 `failure_records`
6. 旧卡片、失效卡片、已替代草稿和已过期通知的回调只记录 `feishu_events`,不写业务对象。
7. 回调处理失败必须写入 `failure_records`
## 11. 敏感信息返回边界 ## 11. 敏感信息返回边界
@@ -216,4 +182,3 @@
2. 手机号和邮箱默认脱敏返回,除非管理员维护页确有需要。 2. 手机号和邮箱默认脱敏返回,除非管理员维护页确有需要。
3. 飞书通知只发送摘要和链接,不发送敏感全文。 3. 飞书通知只发送摘要和链接,不发送敏感全文。
4. 模型调用日志对外接口默认不返回完整 prompt 和原始大段上下文。 4. 模型调用日志对外接口默认不返回完整 prompt 和原始大段上下文。
5. 飞书原始 payload、模型原始请求/响应、OAuth code/state、一次性操作 token 和回调验签密钥的访问边界以 `安全权限日志约定.md` 为准。
-136
View File
@@ -1,136 +0,0 @@
# 安全权限日志约定
本文件是第一版安全、权限、日志脱敏、回调验签、幂等和访问边界的唯一事实源。涉及这些规则时,代码、接口、测试和 Review 结论应以本文件为准。
本文件不展开完整 RBAC、安全合规体系或审计平台,只卡住最小闭环里 AI 和后端最容易写错的硬规则。
## 1. 核心底线
1. AI 只能生成草稿、追问或回复,不能绕过人工确认直接创建事项、创建提醒或发送通知。
2. 业务权限以平台 `users.role`、启用状态和必要白名单为准;飞书身份只用于登录认证、身份匹配和操作人追溯。
3. 权限必须在 DRF permission class 和 service 层同时校验,不能只靠前端隐藏按钮或飞书卡片按钮。
4. 飞书登录回调、事件回调和卡片回调必须验签;验签失败不得执行业务动作。
5. 通知、飞书事件、卡片点击、草稿转换和提醒调度必须幂等。
6. 密钥、token、完整手机号、完整邮箱、一次性操作 token 和回调验签密钥不得进入普通日志。
7. 权限失败、验签失败、通知失败、回调失败、AI 解析失败、模型失败、未授权机器人访问和调度失败必须可复盘。
## 2. 身份边界
| 身份来源 | 可用于 | 不可用于 |
| --- | --- | --- |
| 平台用户 `users` | 登录态、角色、权限、数据范围 | 自然语言称呼解析 |
| `person_mappings` | 称呼、别名、飞书身份、第一批试用人员映射 | 替代平台登录和角色权限 |
| 飞书 open_id / user_id / union_id | 登录认证、消息接收人、回调操作人追溯 | 直接绕过平台权限 |
| AI 对话记忆表 | 上下文恢复、追问、草稿修订辅助 | 替代正式业务表、权限判断和人工确认 |
同一个真实人员如果可以登录平台,应在 `person_mappings.user_id` 绑定对应 `users.id`。飞书身份同步失败时写入 `failure_records`,不得静默降级成“默认有权限”。
## 3. 角色权限
| 动作 | 老板 | 程经理 | 普通员工 | 管理员 / AI 团队 |
| --- | --- | --- | --- | --- |
| 飞书登录 / 平台登录 | 可用 | 可用 | 可用 | 可用 |
| 飞书机器人私聊 AI 秘书 | 可用 | 不开放 | 不开放 | 仅测试或排查 |
| 确认、取消、补充自己的 AI 草稿 | 可用 | 仅待自己确认的复杂事项 | 不开放 | 仅排查,不代替业务确认 |
| 创建事项 | 可用 | 可用 | 不开放给别人 | 可测试或维护 |
| 创建自己的提醒 | 可用 | 可用 | 可用 | 可用 |
| 给别人创建提醒 | 可用 | 可用 | 不开放 | 可测试或维护 |
| 反馈事项或提醒 | 仅自己相关 | 仅自己相关 | 仅自己作为接收人 | 可测试,不代替业务反馈 |
| 查看失败记录原始信息 | 必要业务摘要 | 必要业务摘要 | 不开放 | 可用 |
| 维护人员映射和提示词版本 | 不开放 | 不开放 | 不开放 | 可用 |
普通员工给别人创建事项或提醒、非接收人反馈、非老板使用机器人入口,都必须拒绝并记录。
## 4. 人工确认边界
1. `task` / `reminder` AI 输出必须先生成可确认草稿。
2. 草稿确认前必须校验发起人、草稿状态、缺失字段、接收人映射和当前有效卡片。
3. `note` / `unknown` / `qa` / `realtime_qa` / `need_more_info` / `unsupported` 不得进入事项、提醒或通知闭环。
4. 复杂事项在老板确认后创建 `tasks.status=pending_manager_confirm` 的事项壳,由程经理补齐接收人、内容并触发通知。
5. 草稿、任务、提醒、通知的状态流转必须走 service 层;非法跳转返回 `state_conflict`
## 5. 飞书安全和幂等
1. 飞书回调必须先验签,再解析和执行业务动作。
2. `feishu_events.event_id` 应尽量唯一;没有稳定 `event_id` 时,用 `operator_open_id + action_value + notification_id + target_id` 生成 `idempotency_key` 并唯一约束。
3. 同一飞书事件只处理一次;重复事件只返回既有处理结果,不重复创建草稿、事项、提醒、反馈或通知。
4. 草稿确认类卡片必须匹配 `ai_drafts.active_card_notification_id`
5. 旧卡片、失效卡片、已取消草稿、已替代草稿、已过期草稿和已转换草稿的回调只记录 `feishu_events`,返回“该卡片已失效,请以最新卡片或平台详情为准”,不得写业务对象。
6. 接收人反馈类卡片重复点击必须幂等;是否覆盖最新反馈状态由 feedback service 明确处理,但每次有效点击都应可追溯。
7. 程经理待确认提醒在飞书里只跳转平台,不通过机器人对话分发事项。
## 6. 通知和操作 token
1. `notifications.idempotency_key` 必须按 `purpose` 生成并设置唯一约束。
2. 草稿确认卡片、程经理确认提醒、事项通知、提醒触发通知和补发通知应使用不同 `purpose`
3. 平台反馈页或一次性操作链接只能保存 token hash,例如 `action_token_hash`,不得保存明文 token。
4. 卡片或操作链接过期时写 `expires_at`,被新卡片替代或业务取消时写 `invalidated_at`
5. `expires_at``invalidated_at` 已生效后,回调不得继续确认、转换、通知或反馈,只能记录事件和提示失效。
## 7. 日志脱敏
普通应用日志、`operation_logs.summary``operation_logs.metadata``failure_records.raw_error` 和列表页摘要不得包含:
1. 阿里百炼 API Key。
2. 飞书 App Secret、tenant access token、user access token。
3. 飞书回调验签密钥。
4. OAuth code 和 OAuth state 的完整值。
5. 平台反馈页一次性操作 token 明文。
6. 完整手机号和完整邮箱。
7. 数据库账号密码和连接串密码。
8. 飞书原始 payload 中的敏感字段。
9. 模型 prompt 中的敏感全文或过长背景库全文。
手机号和邮箱在普通日志中只能脱敏展示。密钥、token、OAuth code/state、一次性操作 token 原文只能存在请求处理内存或安全配置中,不得落普通业务表。
## 8. 访问范围
| 信息 | 普通员工 | 老板 | 程经理 | 管理员 / AI 团队 |
| --- | --- | --- | --- | --- |
| 自己相关事项、提醒、反馈 | 可看摘要 | 可看相关记录 | 可看管理范围 | 可排查 |
| 失败记录列表 | 不可见 | 必要摘要 | 必要摘要 | 可见 |
| `model_call_logs` 原始请求和响应 | 不可见 | 不可见 | 不可见 | 可排查 |
| `feishu_events.raw_payload` | 不可见 | 不可见 | 不可见 | 可排查 |
| 完整手机号、邮箱 | 默认不可见 | 默认脱敏 | 默认脱敏 | 维护页按需可见 |
| 操作 token 明文 | 不可见 | 不可见 | 不可见 | 不可见 |
模型日志和飞书原始 payload 可以为排查保存必要结构,但必须通过接口权限和 Admin 权限限制访问,普通列表页默认只展示摘要。
## 9. 必须写失败记录的情况
1. AI 输出无法解析或不符合 schema。
2. 百炼或模型网关重试后仍失败。
3. PostgreSQL AI 记忆、消息或 BotContext 保存/读取失败。
4. 草稿确认后转换事项或提醒失败。
5. 缺少人员映射且阻断确认。
6. 普通员工越权创建事项或提醒。
7. 非接收人提交反馈。
8. 飞书登录失败、验签失败、事件格式异常或回调处理失败。
9. 飞书通知发送失败或补发失败。
10. 定时提醒触发失败或重复触发被跳过需要留痕。
11. 有问题反馈缺少原因。
如果 AI 解析失败时无法创建最小 `parse_failed` 草稿,`failure_records.target_type` 可以使用 `model_call_log` 并关联对应模型调用日志。
## 10. 必须测试的真实约束
每个 plan 完成前,必须说明新增或修改了哪些测试,以及这些测试覆盖了哪些 spec / contract 约定。第一版至少用测试卡住:
1. 权限:普通员工不能给别人创建事项或提醒。
2. 权限:非接收人不能反馈事项或提醒。
3. AI:AI 输出只能生成草稿,不能绕过人工确认直接执行。
4. AI`note` / `unknown` 不创建事项、提醒或通知。
5. 状态:草稿、事项、提醒不能非法跳转。
6. 飞书:回调必须验签,重复事件和重复点击必须幂等。
7. 飞书:旧卡片或失效卡片只记录事件,不写业务对象。
8. 定时:同一个提醒、同一触发时间、同一接收人、同一渠道不能重复触发通知。
9. 事务:通知失败时不能留下被误判为已通知的业务状态。
10. 日志:密钥、token、完整手机号、完整邮箱和一次性操作 token 不进普通日志。
## 11. 第一版不做
1. 不做复杂组织架构和多级 RBAC。
2. 不做独立审计平台。
3. 不做复杂安全合规体系。
4. 不让 AI 自动操作交易系统、公司历史数据库、后台管理系统或其他外部业务系统。
5. 不把飞书机器人开放为程经理或普通员工通用派活入口。
+39 -139
View File
@@ -50,7 +50,6 @@
| `aliases` | JSON 数组,例如 `["东东", "CJN"]` | | `aliases` | JSON 数组,例如 `["东东", "CJN"]` |
| `department` | 部门 | | `department` | 部门 |
| `business_role` | 业务角色,例如行政、程经理、下单员 | | `business_role` | 业务角色,例如行政、程经理、下单员 |
| `manager_user_id` | 第一批试用管理范围归属,可选 |
| `phone` | 手机号,可选 | | `phone` | 手机号,可选 |
| `email` | 邮箱,可选 | | `email` | 邮箱,可选 |
| `feishu_open_id` | 飞书 open_id | | `feishu_open_id` | 飞书 open_id |
@@ -60,13 +59,6 @@
| `is_trial_user` | 是否第一批试用人员 | | `is_trial_user` | 是否第一批试用人员 |
| `note` | 备注 | | `note` | 备注 |
口径:
1. `users` 是登录、角色和权限判断的权威对象。
2. `person_mappings` 是自然语言称呼、手机号 / 邮箱、飞书身份和第一批试用人员映射的权威对象。
3. 同一个真实人员如果可以登录平台,应在 `person_mappings.user_id` 绑定对应 `users.id`
4. 程经理第一批试用管理范围先用 `manager_user_id` 或等价白名单表达,不引入完整组织树。
## 4. feishu_auth_sessions ## 4. feishu_auth_sessions
记录飞书登录过程和平台用户绑定结果。 记录飞书登录过程和平台用户绑定结果。
@@ -84,68 +76,29 @@
| `failure_reason` | 失败原因 | | `failure_reason` | 失败原因 |
| `completed_at` | 完成时间 | | `completed_at` | 完成时间 |
## 5. secretary_conversations ## 5. bot_contexts
老板 AI 秘书会话。第一阶段只有一个老板秘书入口,可先固定 `conversation_id=boss_secretary_default`,但表结构要能支持后续多会话。
| 字段 | 说明 |
| --- | --- |
| `id` | 会话 ID |
| `conversation_id` | 业务会话唯一标识 |
| `boss_id` | 关联老板用户 |
| `source` | `feishu``web``debug` |
| `status` | `active``closed` |
| `last_message_at` | 最近消息时间 |
## 6. secretary_messages
老板输入、AI 回复、AI 追问、普通记录和上下文回顾的统一消息记录。该表只追加,不修改,用于对话回放、上下文回顾和问题复盘。
| 字段 | 说明 |
| --- | --- |
| `id` | 消息 ID |
| `conversation_id` | 关联 `secretary_conversations.conversation_id` |
| `boss_id` | 关联老板用户 |
| `source` | `feishu``web``debug` |
| `message_id` | 外部消息 ID 或内部生成 ID,和 `source` 组成幂等键 |
| `role` | `boss``assistant``system` |
| `text` | 原始输入或 AI 回复 |
| `answer` | AI 给老板的回复,可为空 |
| `intent_type` | `task``reminder``qa``realtime_qa``note``need_more_info``unknown``unsupported``context_summary` |
| `draft_id` | 关联 AI 草稿,可为空 |
| `raw_payload` | 原始请求摘要,避免保存密钥 |
| `bot_context_snapshot` | JSONB,消息发生时的上下文快照 |
| `created_at` | 消息时间 |
## 7. bot_contexts
记录老板机器人私聊中的补充/重说上下文。 记录老板机器人私聊中的补充/重说上下文。
| 字段 | 说明 | | 字段 | 说明 |
| --- | --- | | --- | --- |
| `id` | 上下文 ID | | `id` | 上下文 ID |
| `conversation_id` | 当前老板秘书会话 ID | | `user_id` | 平台用户 ID,第一版应为老板 |
| `boss_id` | PostgreSQL 中的老板用户 ID | | `feishu_open_id` | 飞书发送人 open_id |
| `status` | `empty``awaiting_more_info``awaiting_confirm``awaiting_follow_up``expired``cleared` | | `current_draft_id` | 等待补充的草稿 ID |
| `pending_draft_id` | 当前待补充或待确认的草稿 ID | | `status` | `idle``awaiting_follow_up``expired``closed` |
| `pending_draft_type` | `task``reminder``none` | | `expires_at` | 补充/重说有效期,默认 30 分钟 |
| `last_intent` | 上一条有效意图 | | `last_message_id` | 最近一条飞书消息 ID |
| `expires_at` | 上下文有效期,进入待补充或待确认时重置为 30 分钟后 | | `last_message_text` | 最近一条消息内容 |
| `follow_up_count` | 当前草稿累计追问轮次 |
| `last_message_id` | 最近一次参与上下文判断的消息 ID |
| `extracted_facts` | JSONB,从多轮对话提取的候选事实,只用于草稿修订 |
| `updated_at` | 上下文最近更新时间 |
`awaiting_follow_up` 专用于老板点击飞书确认卡片上的“补充/重说”后,等待下一条消息修订上一版草稿;该状态必须带 `pending_draft_id``expires_at` ## 6. prompt_contexts
## 8. prompt_contexts
维护 AI 秘书调用模型时加载的上下文版本。 维护 AI 秘书调用模型时加载的上下文版本。
| 字段 | 说明 | | 字段 | 说明 |
| --- | --- | | --- | --- |
| `id` | 上下文 ID | | `id` | 上下文 ID |
| `context_type` | `company_background_summary``boss_communication_style``ai_secretary_rules``role_and_alias_rules` | | `context_type` | `company_background``boss_style``business_rules` |
| `version` | 版本号 | | `version` | 版本号 |
| `title` | 标题 | | `title` | 标题 |
| `content` | 实际使用的摘要或规则文本 | | `content` | 实际使用的摘要或规则文本 |
@@ -155,7 +108,7 @@
模型调用日志只保存本次使用的摘要、版本号和必要规则,避免反复保存完整背景库全文。 模型调用日志只保存本次使用的摘要、版本号和必要规则,避免反复保存完整背景库全文。
## 9. ai_drafts ## 7. ai_drafts
保存 AI 从一句话整理出的可确认草稿。 保存 AI 从一句话整理出的可确认草稿。
@@ -163,44 +116,24 @@
| --- | --- | | --- | --- |
| `id` | 草稿 ID | | `id` | 草稿 ID |
| `source` | `platform``feishu_bot` | | `source` | `platform``feishu_bot` |
| `source_message_id` | 飞书或外部入口消息 ID,可选 |
| `created_by` | 发起人 | | `created_by` | 发起人 |
| `parent_draft_id` | 补充/重说生成新草稿时,关联上一版草稿 |
| `raw_input` | 原始输入 | | `raw_input` | 原始输入 |
| `intent` | `task``reminder``qa``realtime_qa``note``need_more_info``unknown``unsupported` | | `draft_type` | `task``reminder``qa``realtime_qa``note``unknown` |
| `draft_type` | `task``reminder``none` |
| `should_create_draft` | `task/reminder` 为 true;其他意图必须为 false |
| `status` | 见状态流转约定 | | `status` | 见状态流转约定 |
| `title` | 草稿标题 | | `title` | 草稿标题 |
| `content` | 事项或提醒内容 | | `content` | 事项或提醒内容 |
| `receiver_candidates` | JSON,接收人候选和置信度 | | `receiver_candidates` | JSON,接收人候选和置信度 |
| `receiver_text` | 未映射成功时保留老板原始称呼 | | `scheduled_time` | 提醒或期望处理时间,可为空 |
| `selected_receiver_id` | 人工确认后的接收人,可选 | | `repeat_rule` | `none``daily``weekly``monthly` |
| `scheduled_at` | 提醒时间,可为空;任务可使用 `schedule_text` 表达期望时间 |
| `schedule_text` | 老板原始时间表达 |
| `recurrence_type` | `none``daily``weekly``monthly` |
| `requires_feedback` | 是否需要反馈 | | `requires_feedback` | 是否需要反馈 |
| `route_type` | `none``direct_after_boss_confirm``manager_confirm_required` | | `route_type` | `direct_notify``manager_confirm``needs_clarification` |
| `need_manager_confirm` | 是否需要程经理确认 |
| `missing_fields` | JSON 数组 | | `missing_fields` | JSON 数组 |
| `questions` | JSON 数组,最多 3 个;确认草稿一般为空 | | `follow_up_questions` | JSON 数组,最多 3 个 |
| `answer` | 给老板看的回复摘要;不得写“已通知、已创建、已发送”等执行语义 | | `answer` | `qa` / `realtime_qa` 的回答 |
| `active_card_notification_id` | 当前有效确认卡片通知,可选 | | `parent_draft_id` | 补充/重说生成的新草稿指向上一版 |
| `superseded_by_draft_id` | 被补充/重说的新草稿替代时,指向新草稿 |
| `model_call_log_id` | 关联模型调用日志 | | `model_call_log_id` | 关联模型调用日志 |
| `confirmed_by` | 确认人 |
| `confirmed_at` | 确认时间 |
| `cancelled_reason` | 取消原因 |
草稿生命周期口径: ## 8. model_call_logs
1. 每个草稿同一时间只能有一张有效确认卡片,对应 `active_card_notification_id`
2. 老板点击“补充/重说”后,原草稿进入 `awaiting_follow_up`,原确认卡片应失效。
3. 30 分钟内收到补充消息后,系统生成新草稿,`parent_draft_id` 指向原草稿;原草稿进入 `superseded``superseded_by_draft_id` 指向新草稿。
4. 超过 30 分钟仍未收到补充消息时,`bot_contexts` 标记为 `expired`,原草稿进入 `expired`;老板下一条消息按新输入处理。
5. `note` / `unknown` 不进入事项、提醒或通知闭环;如需留痕,只能保存回复、消息、模型调用日志或最小草稿记录。
## 10. model_call_logs
记录模型输入输出、模型名、耗时和结果。 记录模型输入输出、模型名、耗时和结果。
@@ -209,33 +142,27 @@
| `id` | 日志 ID | | `id` | 日志 ID |
| `provider` | `bailian` | | `provider` | `bailian` |
| `model_name` | 模型名 | | `model_name` | 模型名 |
| `prompt_version` | 提示词版本 | | `prompt_context_snapshot` | 本次使用的上下文版本和摘要 |
| `background_context_version` | 公司背景摘要版本 |
| `boss_style_version` | 老板沟通风格版本 |
| `ai_secretary_rules_version` | AI 秘书业务规则版本 |
| `context_snapshot` | 本次使用的上下文版本和摘要 |
| `input_text` | 用户原始输入 |
| `request_payload` | 请求摘要,避免保存密钥 | | `request_payload` | 请求摘要,避免保存密钥 |
| `response_payload` | 模型原始响应结构 | | `raw_response` | 模型原始响应 |
| `parsed_result` | 后端解析后的 `AiSecretaryResponse` JSON | | `parsed_json` | 解析后的 JSON |
| `status` | `success``failed` | | `status` | `success``failed` |
| `error_message` | 失败原因 | | `failure_reason` | 失败原因 |
| `latency_ms` | 耗时 | | `latency_ms` | 耗时 |
| `created_by` | 调用发起人 | | `created_by` | 调用发起人 |
## 11. tasks ## 9. tasks
需要接收人处理并反馈的事情。 需要接收人处理并反馈的事情。
| 字段 | 说明 | | 字段 | 说明 |
| --- | --- | | --- | --- |
| `id` | 事项 ID | | `id` | 事项 ID |
| `source_type` | `ai_draft``manual` |
| `source_draft_id` | 来源草稿,可选 | | `source_draft_id` | 来源草稿,可选 |
| `title` | 标题 | | `title` | 标题 |
| `content` | 事项内容 | | `content` | 事项内容 |
| `initiator_id` | 发起人 | | `initiator_id` | 发起人 |
| `receiver_id` | 接收人;复杂事项在程经理确认前可为空 | | `receiver_id` | 接收人 |
| `manager_id` | 复杂事项确认人,可选 | | `manager_id` | 复杂事项确认人,可选 |
| `status` | 见状态流转约定 | | `status` | 见状态流转约定 |
| `visible_feedback_status` | `received``in_progress``completed``problem` | | `visible_feedback_status` | `received``in_progress``completed``problem` |
@@ -246,16 +173,13 @@
| `completed_at` | 完成时间 | | `completed_at` | 完成时间 |
| `problem_reason` | 有问题原因,可选 | | `problem_reason` | 有问题原因,可选 |
复杂事项必须在老板确认后创建 `status=pending_manager_confirm` 的事项壳,不得一直停留在草稿中等待程经理补齐。 ## 10. reminders
## 12. reminders
未来某个时间触发的提醒。 未来某个时间触发的提醒。
| 字段 | 说明 | | 字段 | 说明 |
| --- | --- | | --- | --- |
| `id` | 提醒 ID | | `id` | 提醒 ID |
| `source_type` | `ai_draft``manual` |
| `source_draft_id` | 来源草稿,可选 | | `source_draft_id` | 来源草稿,可选 |
| `related_task_id` | 关联事项,可选 | | `related_task_id` | 关联事项,可选 |
| `title` | 标题 | | `title` | 标题 |
@@ -263,14 +187,14 @@
| `initiator_id` | 发起人 | | `initiator_id` | 发起人 |
| `receiver_id` | 接收人 | | `receiver_id` | 接收人 |
| `status` | 见状态流转约定 | | `status` | 见状态流转约定 |
| `scheduled_at` | 首次提醒时间 | | `remind_at` | 首次提醒时间 |
| `next_trigger_at` | 下一次触发时间 | | `next_trigger_at` | 下一次触发时间 |
| `recurrence_type` | `none``daily``weekly``monthly` | | `repeat_rule` | `none``daily``weekly``monthly` |
| `requires_feedback` | 是否需要反馈 | | `requires_feedback` | 是否需要反馈 |
| `last_triggered_at` | 最近触发时间 | | `last_triggered_at` | 最近触发时间 |
| `cancelled_at` | 取消时间 | | `cancelled_at` | 取消时间 |
## 13. notifications ## 11. notifications
飞书或平台内通知发送记录。 飞书或平台内通知发送记录。
@@ -279,35 +203,18 @@
| `id` | 通知 ID | | `id` | 通知 ID |
| `target_type` | `ai_draft``task``reminder``failure_record` | | `target_type` | `ai_draft``task``reminder``failure_record` |
| `target_id` | 关联对象 ID | | `target_id` | 关联对象 ID |
| `purpose` | `draft_confirm``manager_confirm``task_notify``reminder_trigger` |
| `receiver_id` | 接收人 | | `receiver_id` | 接收人 |
| `channel` | `feishu_personal``feishu_group``platform` | | `channel` | `feishu``platform` |
| `message_type` | `text``card` | | `message_type` | `text``card` |
| `title` | 通知标题 |
| `summary` | 通知摘要 | | `summary` | 通知摘要 |
| `link_url` | 平台详情链接 | | `link_url` | 平台详情链接 |
| `card_id` | 飞书卡片 ID,可选 | | `status` | `pending``sent``failed``retrying``cancelled` |
| `status` | `pending``sending``sent``failed``retrying``cancelled``expired` | | `idempotency_key` | 幂等键 |
| `trigger_time` | 提醒触发时间;非提醒触发通知可为空 |
| `idempotency_key` | 幂等键,按通知目的生成并唯一约束 |
| `action_token_hash` | 平台反馈页一次性操作 token 的 hash,可选 |
| `feishu_message_id` | 飞书消息 ID | | `feishu_message_id` | 飞书消息 ID |
| `sent_at` | 发送时间 | | `sent_at` | 发送时间 |
| `expires_at` | 卡片或通知操作过期时间,可选 |
| `invalidated_at` | 被新卡片替代或业务取消时的失效时间,可选 |
| `failure_reason` | 失败原因 | | `failure_reason` | 失败原因 |
| `retry_count` | 重试次数 |
| `last_retry_at` | 最近重试时间 |
提醒触发类通知的幂等键格式建议: ## 12. feedbacks
```text
reminder:{reminder_id}:{trigger_time}:{receiver_id}:{channel}
```
任务通知、草稿确认卡片和程经理待确认提醒也必须生成各自的幂等键,避免重复点击、重复补发或回调重放造成多条有效通知。
## 14. feedbacks
接收人的反馈状态和问题原因。 接收人的反馈状态和问题原因。
@@ -323,7 +230,7 @@ reminder:{reminder_id}:{trigger_time}:{receiver_id}:{channel}
| `notification_id` | 关联通知 | | `notification_id` | 关联通知 |
| `created_at` | 反馈时间 | | `created_at` | 反馈时间 |
## 15. failure_records ## 13. failure_records
AI、通知、回调、调度等失败原因。 AI、通知、回调、调度等失败原因。
@@ -341,9 +248,7 @@ AI、通知、回调、调度等失败原因。
| `handled_at` | 处理时间 | | `handled_at` | 处理时间 |
| `handle_result` | 处理结果 | | `handle_result` | 处理结果 |
`target_type` 可使用 `ai_draft``task``reminder``notification``feishu_event``model_call_log` 等实际对象类型。AI 解析失败时,如果无法创建最小 `parse_failed` 草稿,可关联 `model_call_log` ## 14. operation_logs
## 16. operation_logs
人工确认、修改、取消、补发等过程留痕。 人工确认、修改、取消、补发等过程留痕。
@@ -360,7 +265,7 @@ AI、通知、回调、调度等失败原因。
| `metadata` | JSON,避免敏感明文 | | `metadata` | JSON,避免敏感明文 |
| `created_at` | 操作时间 | | `created_at` | 操作时间 |
## 17. feishu_events ## 15. feishu_events
飞书回调原始事件和处理结果。 飞书回调原始事件和处理结果。
@@ -371,7 +276,6 @@ AI、通知、回调、调度等失败原因。
| `event_type` | 事件类型 | | `event_type` | 事件类型 |
| `operator_open_id` | 操作人 open_id | | `operator_open_id` | 操作人 open_id |
| `action_value` | 按钮值 | | `action_value` | 按钮值 |
| `idempotency_key` | 事件处理幂等键,可选 |
| `target_type` | `ai_draft``task``reminder``auth``bot` | | `target_type` | `ai_draft``task``reminder``auth``bot` |
| `target_id` | 关联业务对象 | | `target_id` | 关联业务对象 |
| `notification_id` | 关联通知 | | `notification_id` | 关联通知 |
@@ -380,13 +284,9 @@ AI、通知、回调、调度等失败原因。
| `process_result` | 处理结果 | | `process_result` | 处理结果 |
| `created_at` | 接收时间 | | `created_at` | 接收时间 |
`event_id` 应尽量设置唯一约束;如果飞书某类回调没有稳定 `event_id`,则用 `operator_open_id + action_value + notification_id + target_id` 生成 `idempotency_key` 并唯一约束。重复事件只返回已处理结果,不重复创建草稿、事项、提醒、反馈或通知。 ## 16. 敏感字段规则
## 18. 敏感字段规则
1. `phone``email` 可入库,但普通日志和普通列表接口默认脱敏。 1. `phone``email` 可入库,但普通日志和普通列表接口默认脱敏。
2. API Key、App Secret、飞书 token、飞书回调验签密钥、OAuth code/state、一次性操作 token 明文不得入库到业务表或普通日志。 2. API Key、App Secret、飞书 token 不得入库到业务表或普通日志。
3. `raw_error``metadata``request_payload``response_payload` 必须避免密钥和完整个人联系方式。 3. `raw_error``metadata``request_payload` 必须避免密钥和完整个人联系方式。
4. 飞书通知内容只保存和发送摘要,不展开敏感全文。 4. 飞书通知内容只保存和发送摘要,不展开敏感全文。
5. `model_call_logs` 原始请求/响应和 `feishu_events.raw_payload` 仅供管理员 / AI 团队排查,普通员工不可见,老板和程经理只在必要业务范围内查看摘要。
6. 平台反馈页一次性操作 token 只能保存 hash,不能保存明文。
+26 -49
View File
@@ -14,41 +14,35 @@
| 状态 | 含义 | | 状态 | 含义 |
| --- | --- | | --- | --- |
| `pending_confirmation` | 草稿已整理完成,等待老板确认、修改、取消或补充/重说 | | `pending_confirmation` | 等待老板确认 |
| `awaiting_follow_up` | 老板已点击补充/重说,等待 30 分钟内下一条补充消息 | | `awaiting_follow_up` | 等待老板补充/重说 |
| `confirmed` | 已人工确认,等待转换为事项或提醒 | | `pending_manager_confirm` | 等待程经理确认 |
| `converted` | 已转换为事项或提醒;复杂事项已创建 `pending_manager_confirm` 事项壳也视为已转换 | | `confirmed` | 已人工确认 |
| `converted` | 已转换为事项或提醒 |
| `cancelled` | 已取消 | | `cancelled` | 已取消 |
| `answered` | 已直接回复或留痕,不进入事项、提醒或通知闭环 | | `parse_failed` | AI 输出解析失败 |
| `superseded` | 已被补充/重说生成的新草稿替代 | | `expired` | 补充/重说上下文过期 |
| `expired` | 补充/重说等待超时,旧确认卡片不可继续使用 |
| `parse_failed` | AI 解析或草稿处理失败,仅用于调试追踪 |
允许流转: 允许流转:
```text ```text
pending_confirmation -> confirmed
pending_confirmation -> awaiting_follow_up pending_confirmation -> awaiting_follow_up
pending_confirmation -> pending_manager_confirm
pending_confirmation -> confirmed
pending_confirmation -> cancelled pending_confirmation -> cancelled
pending_confirmation -> parse_failed pending_confirmation -> parse_failed
awaiting_follow_up -> superseded awaiting_follow_up -> pending_confirmation
awaiting_follow_up -> expired awaiting_follow_up -> expired
awaiting_follow_up -> cancelled pending_manager_confirm -> confirmed
pending_manager_confirm -> cancelled
confirmed -> converted confirmed -> converted
confirmed -> parse_failed
``` ```
约束: 约束:
1. `parse_failed``cancelled``converted``answered``superseded``expired` 为终态。 1. `parse_failed``cancelled``converted` 为终态。
2. 未进入 `confirmed` 的草稿不得转换。 2. 未进入 `confirmed` 的草稿不得转换。
3. `note``unknown``qa``realtime_qa``need_more_info``unsupported` 不进入事项、提醒或通知闭环;如需留痕,只能使用 `answered``parse_failed` 3. 补充/重说生成的新草稿应写 `parent_draft_id`
4. 老板点击补充/重说后,原草稿进入 `awaiting_follow_up`,并使原确认卡片失效。
5. 30 分钟内收到补充消息后,新草稿 `parent_draft_id` 指向原草稿,原草稿进入 `superseded`
6. 超过 30 分钟后,原草稿进入 `expired`,老板下一条消息按新输入处理。
7. 飞书回调确认、取消、补充/重说前,必须校验回调来自 `active_card_notification_id` 对应的当前有效卡片。
8. 复杂事项不使用草稿状态等待程经理确认;老板确认后应创建 `tasks.status=pending_manager_confirm` 的事项壳。
9. `answered``parse_failed` 可以作为最小留痕草稿的初始终态,不参与确认和转换。
## 3. 事项状态 ## 3. 事项状态
@@ -154,28 +148,21 @@ triggered -> expired
| 状态 | 含义 | | 状态 | 含义 |
| --- | --- | | --- | --- |
| `pending` | 待发送 | | `pending` | 待发送 |
| `sending` | 发送中 |
| `sent` | 已发送 | | `sent` | 已发送 |
| `failed` | 发送失败 | | `failed` | 发送失败 |
| `retrying` | 补发中 | | `retrying` | 补发中 |
| `cancelled` | 已取消 | | `cancelled` | 已取消 |
| `expired` | 卡片或通知操作已过期 |
允许流转: 允许流转:
```text ```text
pending -> sending
pending -> sent pending -> sent
pending -> failed pending -> failed
sending -> sent
sending -> failed
failed -> retrying failed -> retrying
retrying -> sent retrying -> sent
retrying -> failed retrying -> failed
pending -> cancelled pending -> cancelled
failed -> cancelled failed -> cancelled
pending -> expired
sent -> expired
``` ```
约束: 约束:
@@ -183,8 +170,6 @@ sent -> expired
1. `sent` 不得重复发送同一通知。 1. `sent` 不得重复发送同一通知。
2. 补发必须保留原失败记录和新发送结果。 2. 补发必须保留原失败记录和新发送结果。
3. 飞书通知必须使用幂等键。 3. 飞书通知必须使用幂等键。
4. `expired``cancelled` 通知不得继续处理卡片确认、反馈或补充/重说。
5. 被新卡片替代或业务取消时,必须写 `invalidated_at`
通知幂等键建议: 通知幂等键建议:
@@ -243,21 +228,17 @@ processing -> cancelled
| 类型 | 含义 | | 类型 | 含义 |
| --- | --- | | --- | --- |
| `ai_parse_failed` | AI 输出解析失败 | | `ai_parse_failed` | AI 输出解析失败 |
| `ai_model_failed` | 百炼或模型网关超时、网络失败、服务异常,重试后仍失败 | | `person_mapping_missing` | 缺少人员映射 |
| `bot_message_failed` | 入口消息结构缺失、无法解析文本或 sender 信息 |
| `missing_person_mapping` | 缺少人员映射 |
| `memory_store_failed` | PostgreSQL AI 记忆表、上下文或快照保存/读取失败 |
| `draft_convert_failed` | 老板或程经理确认后,转换任务/提醒失败 |
| `feishu_auth_failed` | 飞书登录失败 | | `feishu_auth_failed` | 飞书登录失败 |
| `feishu_send_failed` | 飞书通知失败 | | `feishu_notify_failed` | 飞书通知失败 |
| `feishu_callback_failed` | 飞书回调处理失败 | | `feishu_callback_failed` | 飞书回调处理失败 |
| `feishu_signature_invalid` | 飞书验签失败 | | `feishu_signature_invalid` | 飞书验签失败 |
| `reminder_trigger_failed` | 定时提醒触发失败 | | `scheduler_trigger_failed` | 定时提醒触发失败 |
| `bot_unauthorized` | 非老板使用机器人入口 | | `unauthorized_bot_access` | 非老板使用机器人入口 |
| `follow_up_expired` | 补充/重说上下文过期 | | `bot_context_expired` | 补充/重说上下文过期 |
| `user_feedback_problem` | 用户反馈有问题 | | `user_feedback_problem` | 用户反馈有问题 |
| `permission_error` | 权限拒绝 | | `permission_denied` | 权限拒绝 |
| `system_error` | 未归类系统异常 | | `other` | 其他 |
## 11. 飞书事件处理状态 ## 11. 飞书事件处理状态
@@ -271,10 +252,8 @@ processing -> cancelled
约束: 约束:
1. 同一 `event_id` 只处理一次。 1. 同一 `event_id` 只处理一次。
2. 如果飞书某类回调没有稳定 `event_id`,必须使用 `idempotency_key` 做事件幂等 2. 验签失败不得执行业务动作
3. 验签失败不得执行业务动作 3. 处理失败必须写失败记录
4. 旧卡片、失效卡片、已替代草稿和已过期通知的回调只记录事件并标记 `ignored`,不得写业务对象。
5. 处理失败必须写失败记录。
## 12. 用户和配置状态 ## 12. 用户和配置状态
@@ -313,9 +292,7 @@ Prompt 上下文状态:
| 状态 | 含义 | | 状态 | 含义 |
| --- | --- | | --- | --- |
| `empty` | 当前没有可补充或可确认的草稿 | | `idle` | 空闲 |
| `awaiting_more_info` | AI 已追问,等待老板补充 | | `awaiting_follow_up` | 等待补充/重说 |
| `awaiting_confirm` | 草稿已整理完成,等待老板确认、修改、取消或转经理 |
| `awaiting_follow_up` | 老板点击补充/重说后,等待下一条消息修订上一版草稿 |
| `expired` | 已过期 | | `expired` | 已过期 |
| `cleared` | 草稿已确认、取消或转换完成,上下文已清空 | | `closed` | 已关闭 |
@@ -6,7 +6,7 @@
**Architecture:** 使用 Django 模块化单体。DRF view 负责请求入口,serializer 负责入参出参校验,service 负责权限、状态流转、事务和幂等,client 负责阿里百炼和飞书外部调用,audit 负责操作日志和失败记录。 **Architecture:** 使用 Django 模块化单体。DRF view 负责请求入口,serializer 负责入参出参校验,service 负责权限、状态流转、事务和幂等,client 负责阿里百炼和飞书外部调用,audit 负责操作日志和失败记录。
**Tech Stack:** 部署 Python 3.12.13、本地开发 Python 3.11/3.12、Django 5.2 LTS、Django REST Framework、Django ORM、PostgreSQL 18.x、Django management command scheduler、阿里百炼 API、飞书开放平台。 **Tech Stack:** 部署 Python 3.12.13、本地开发 Python 3.11/3.12、Django 5.2 LTS、Django REST Framework、Django ORM、MySQL 8、Django management command scheduler、阿里百炼 API、飞书开放平台。
--- ---
@@ -22,9 +22,8 @@
8. `docs/contracts/API接口约定.md` 8. `docs/contracts/API接口约定.md`
9. `docs/contracts/数据对象约定.md` 9. `docs/contracts/数据对象约定.md`
10. `docs/contracts/状态流转约定.md` 10. `docs/contracts/状态流转约定.md`
11. `docs/contracts/安全权限日志约定.md` 11. `docs/checklists/AI生成代码检查清单.md`
12. `docs/checklists/AI生成代码检查清单.md` 12. `docs/checklists/后端Review清单.md`
13. `docs/checklists/后端Review清单.md`
## 范围边界 ## 范围边界
@@ -48,7 +47,6 @@
2. 真实联调前必须确认飞书正式应用、HTTPS 回调地址、个人消息权限、交互卡片权限、老板 / 程经理 / 第一批试用人员映射,以及百炼 API Key 环境变量。 2. 真实联调前必须确认飞书正式应用、HTTPS 回调地址、个人消息权限、交互卡片权限、老板 / 程经理 / 第一批试用人员映射,以及百炼 API Key 环境变量。
3. Docker / 部署环境固定 Python 3.12.13;本地开发允许 Python 3.11 或 3.12,但提交前必须在 Python 3.12.13 环境跑完整测试。 3. Docker / 部署环境固定 Python 3.12.13;本地开发允许 Python 3.11 或 3.12,但提交前必须在 Python 3.12.13 环境跑完整测试。
4. scheduler 必须作为独立进程运行,同一环境同一应用库同时只能启动一个 scheduler 进程。 4. scheduler 必须作为独立进程运行,同一环境同一应用库同时只能启动一个 scheduler 进程。
5. tests 是真实约束;每个任务完成前必须说明新增或修改了哪些测试、覆盖哪些 spec / contract 约定和如何运行。
## 文件结构 ## 文件结构
@@ -139,7 +137,7 @@ apps/feishu/signature.py
```txt ```txt
Django==5.2.15 Django==5.2.15
djangorestframework==3.17.1 djangorestframework==3.17.1
psycopg[binary] mysqlclient==2.2.8
asgiref==3.11.1 asgiref==3.11.1
sqlparse==0.5.5 sqlparse==0.5.5
tzdata==2026.2 tzdata==2026.2
@@ -182,7 +180,7 @@ Expected: `System check identified no issues`。
--- ---
### Task 2: 账号、人员映射、Prompt、AI 记忆和审计基础模型 ### Task 2: 账号、人员映射、Prompt 和审计基础模型
**Files:** **Files:**
- Modify: `config/settings.py` - Modify: `config/settings.py`
@@ -190,7 +188,6 @@ Expected: `System check identified no issues`。
- Create: `apps/people/models.py` - Create: `apps/people/models.py`
- Create: `apps/prompts/models.py` - Create: `apps/prompts/models.py`
- Create: `apps/audit/models.py` - Create: `apps/audit/models.py`
- Create: `apps/drafts/models.py`
- Create: `apps/accounts/admin.py` - Create: `apps/accounts/admin.py`
- Create: `apps/people/admin.py` - Create: `apps/people/admin.py`
- Create: `apps/prompts/admin.py` - Create: `apps/prompts/admin.py`
@@ -204,21 +201,17 @@ Expected: `System check identified no issues`。
- [ ] **Step 2: 实现 PersonMapping** - [ ] **Step 2: 实现 PersonMapping**
字段必须覆盖 `person_mappings`,并支持别名 JSON`user_id``manager_user_id``users` 负责登录和权限,`person_mappings` 负责称呼、飞书身份和第一批试用人员映射 字段必须覆盖 `person_mappings`,并支持别名 JSON。
- [ ] **Step 3: 实现 PromptContext** - [ ] **Step 3: 实现 PromptContext**
支持 `company_background_summary``boss_communication_style``ai_secretary_rules``role_and_alias_rules` 类上下文,只允许同类一个 `active` 版本。 支持 `company_background``boss_style``business_rules` 类上下文,只允许同类一个 `active` 版本。
- [ ] **Step 4: 实现 AI 对话记忆基础表** - [ ] **Step 4: 实现 FailureRecord、OperationLog、ModelCallLog**
实现 `secretary_conversations``secretary_messages``bot_contexts``secretary_messages` 只追加,不修改;`bot_contexts` 保存当前待确认草稿、30 分钟过期时间、`follow_up_count``extracted_facts`。JSON 弹性字段使用 PostgreSQL `jsonb`,不得用内存隐藏保存可确认状态。
- [ ] **Step 5: 实现 FailureRecord、OperationLog、ModelCallLog**
失败类型和状态必须来自 `docs/contracts/状态流转约定.md` 失败类型和状态必须来自 `docs/contracts/状态流转约定.md`
- [ ] **Step 6: 写权限、AI 记忆和脱敏测试** - [ ] **Step 5: 写权限和脱敏测试**
测试必须覆盖: 测试必须覆盖:
@@ -226,27 +219,25 @@ Expected: `System check identified no issues`。
2. 普通日志工具不会输出完整手机号。 2. 普通日志工具不会输出完整手机号。
3. 普通日志工具不会输出完整邮箱。 3. 普通日志工具不会输出完整邮箱。
4. 日志元数据不会保存 API Key、App Secret 或飞书 token。 4. 日志元数据不会保存 API Key、App Secret 或飞书 token。
5. `secretary_messages` 幂等键 `source + message_id` 不重复写入。
6. PostgreSQL AI 记忆读写失败时返回 `memory_store_failed`,不继续依赖内存确认草稿。
- [ ] **Step 7: 生成并运行迁移** - [ ] **Step 6: 生成并运行迁移**
Run: Run:
```bash ```bash
python manage.py makemigrations python manage.py makemigrations
python manage.py migrate python manage.py migrate
python manage.py test apps.accounts apps.audit apps.drafts python manage.py test apps.accounts apps.audit
``` ```
Expected: migrations 成功,权限、审计和 AI 记忆测试通过。 Expected: migrations 成功,测试通过。
--- ---
### Task 3: AiSecretaryAgent、AI 输出校验和统一消息入口 ### Task 3: AI 草稿模型、AI 输出校验和解析服务
**Files:** **Files:**
- Modify: `apps/drafts/models.py` - Create: `apps/drafts/models.py`
- Create: `apps/drafts/serializers.py` - Create: `apps/drafts/serializers.py`
- Create: `apps/drafts/ai_client.py` - Create: `apps/drafts/ai_client.py`
- Create: `apps/drafts/services.py` - Create: `apps/drafts/services.py`
@@ -255,58 +246,50 @@ Expected: migrations 成功,权限、审计和 AI 记忆测试通过。
- Test: `apps/drafts/tests/test_ai_output_validator.py` - Test: `apps/drafts/tests/test_ai_output_validator.py`
- Test: `apps/drafts/tests/test_parse_service.py` - Test: `apps/drafts/tests/test_parse_service.py`
- [ ] **Step 1: 实现 AiDraft 模型和 AiSecretaryResponse 结构** - [ ] **Step 1: 实现 AiDraft 模型**
字段必须覆盖 `ai_drafts`,状态必须来自 `状态流转约定.md``qa/realtime_qa/note/need_more_info/unknown/unsupported/context_summary` 不进入事项、提醒或通知闭环,只写 `secretary_messages``model_call_logs`、必要 `operation_logs`,或按 contract 写最小留痕草稿。 字段必须覆盖 `ai_drafts`,状态必须来自 `状态流转约定.md`
- [ ] **Step 2: 实现 AI 输出 validator** - [ ] **Step 2: 实现 AI 输出 validator**
validator 必须校验: validator 必须校验:
1. JSON 可解析。 1. JSON 可解析。
2. `intent` 只能是 `task/reminder/qa/realtime_qa/note/need_more_info/unknown/unsupported` 2. `draft_type` 在允许范围内
3. `task` / `reminder` 才允许 `should_create_draft = true``draft_type` 必须对应为 `task``reminder` 3. `task` / `reminder` 才允许 `should_create_draft = true`
4. `qa/realtime_qa/note/need_more_info/unknown/unsupported` 必须 `should_create_draft=false``draft_type=none` 4. `qa` / `realtime_qa` 必须 `answer`,且不得创建事项、提醒或通知
5. `need_more_info``questions` 至少 1 个、最多 3 个;其他 intent 时 `questions` 为空数组 5. `follow_up_questions` 最多 3 个
6. `task``route_type` 只能是 `direct_after_boss_confirm``manager_confirm_required`,其他 intent 为 `none` 6. `route_type` 在允许范围内
7. `answer` 不得出现“已通知、已创建、已发送”等执行语义 7. 结构化字段不得包含明显游戏化表达
8. 结构化字段不得包含明显游戏化表达,也不得编造老板没说过的业务结论。
- [ ] **Step 3: 实现可 mock 的 ai_client** - [ ] **Step 3: 实现可 mock 的 ai_client**
`ai_client` 只封装阿里百炼调用,不修改业务表。测试中必须使用 mock,不访问真实网络。 `ai_client` 只封装阿里百炼调用,不修改业务表。测试中必须使用 mock,不访问真实网络。
- [ ] **Step 4: 实现 AiSecretaryAgent service** - [ ] **Step 4: 实现 parse service**
service 流程: service 流程:
```text ```text
记录原始消息 加载 active prompt_contexts
-> 按 source + message_id 做幂等检查
-> 校验老板角色
-> 读取 BotContext
-> 判断 follow_up / new_request / qa / realtime_qa / note / unknown / unsupported / command
-> 组装 PromptContext
-> 调用 ai_client -> 调用 ai_client
-> 写 model_call_logs -> 写 model_call_logs
-> 校验 AiSecretaryResponse JSON -> 校验 JSON
-> 生成草稿 / 追问 / 直接回复 -> 成功创建 ai_drafts
-> 写 secretary_messages、operation_logs 和必要 failure_records -> 失败创建 parse_failed 草稿和 failure_records
``` ```
- [ ] **Step 5: 实现 API** - [ ] **Step 5: 实现 API**
实现: 实现:
1. `POST /api/secretary/handle-message` 1. `POST /api/ai-drafts/parse`
2. `GET /api/ai-drafts` 2. `GET /api/ai-drafts`
3. `GET /api/ai-drafts/{id}` 3. `GET /api/ai-drafts/{id}`
4. `PATCH /api/ai-drafts/{id}` 4. `PATCH /api/ai-drafts/{id}`
5. `POST /api/ai-drafts/{id}/cancel` 5. `POST /api/ai-drafts/{id}/cancel`
6. `POST /api/ai-drafts/{id}/follow-up` 6. `POST /api/ai-drafts/{id}/follow-up`
兼容接口 `/api/ai-drafts/parse``/api/demo/boss-message` 可以保留,但内部必须转发到 `/api/secretary/handle-message`
- [ ] **Step 6: 跑测试** - [ ] **Step 6: 跑测试**
Run: Run:
@@ -338,7 +321,7 @@ Expected: AI 输出校验、解析失败、实时问答、补充/重说测试全
- [ ] **Step 1: 实现模型** - [ ] **Step 1: 实现模型**
字段必须覆盖 `tasks``reminders``notifications``feedbacks``notifications` 必须包含 `target_type=ai_draft``purpose``trigger_time``idempotency_key``action_token_hash``expires_at``invalidated_at` 等字段。 字段必须覆盖 `tasks``reminders``notifications``feedbacks`
- [ ] **Step 2: 实现事项 service** - [ ] **Step 2: 实现事项 service**
@@ -367,7 +350,7 @@ Expected: AI 输出校验、解析失败、实时问答、补充/重说测试全
1. 生成飞书通知记录。 1. 生成飞书通知记录。
2. 使用幂等键避免重复通知。 2. 使用幂等键避免重复通知。
3. 接收人反馈。 3. 接收人反馈。
4. 非接收人反馈返回 `permission_denied`,并写 `FailureRecord(permission_error)` 4. 非接收人反馈返回 `permission_denied`
- [ ] **Step 5: 实现 API** - [ ] **Step 5: 实现 API**
@@ -411,7 +394,7 @@ Expected: 权限、状态、反馈原因和通知失败测试通过。
- [ ] **Step 3: 实现事件幂等** - [ ] **Step 3: 实现事件幂等**
同一 `event_id` 重放时只返回已有处理结果,不重复创建草稿、事项、提醒或反馈。没有稳定 `event_id` 的卡片回调必须使用 `idempotency_key` 同一 `event_id` 重放时只返回已有处理结果,不重复创建草稿、事项、提醒或反馈。
- [ ] **Step 4: 实现老板机器人入口** - [ ] **Step 4: 实现老板机器人入口**
@@ -421,7 +404,7 @@ Expected: 权限、状态、反馈原因和通知失败测试通过。
不调用 AI 不调用 AI
不生成草稿 不生成草稿
回复当前入口仅对老板开放 回复当前入口仅对老板开放
bot_unauthorized 失败记录 写 unauthorized_bot_access 失败记录
``` ```
- [ ] **Step 5: 实现卡片回调** - [ ] **Step 5: 实现卡片回调**
@@ -433,7 +416,6 @@ Expected: 权限、状态、反馈原因和通知失败测试通过。
3. 老板取消草稿。 3. 老板取消草稿。
4. 接收人反馈事项或提醒。 4. 接收人反馈事项或提醒。
5. 程经理待确认提醒只跳转平台。 5. 程经理待确认提醒只跳转平台。
6. 旧卡片、失效卡片、已替代草稿和已过期通知只记录 `feishu_events`,不写业务对象。
- [ ] **Step 6: 跑测试** - [ ] **Step 6: 跑测试**
@@ -461,11 +443,11 @@ Expected: 验签、幂等、非老板访问、卡片反馈测试通过。
- [ ] **Step 1: 实现草稿确认和转换** - [ ] **Step 1: 实现草稿确认和转换**
`confirmed -> converted` 只允许执行一次。`task` 草稿转换为事项,`reminder` 草稿转换为提醒。转换失败时不回滚老板确认,写 `FailureRecord(draft_convert_failed)` `confirmed -> converted` 只允许执行一次。`task` 草稿转换为事项,`reminder` 草稿转换为提醒。
- [ ] **Step 2: 实现复杂事项转程经理确认** - [ ] **Step 2: 实现复杂事项转程经理确认**
`route_type = manager_confirm_required` 时,老板确认后草稿进入 `confirmed`,转换时先创建 `tasks.status=pending_manager_confirm` 的事项壳,并生成给程经理的飞书提醒;事项壳创建成功后草稿进入 `converted`。程经理确认事项壳后补齐接收人和内容,再进入通知流程 `route_type = manager_confirm` 时创建程经理待确认记录,并生成给程经理的飞书提醒
- [ ] **Step 3: 实现 scheduler command** - [ ] **Step 3: 实现 scheduler command**
@@ -515,15 +497,14 @@ Expected: 草稿不重复转换、提醒不重复触发、通知补发测试通
`config/urls.py` 挂载: `config/urls.py` 挂载:
1. `/api/secretary/` 1. `/api/ai-drafts/`
2. `/api/ai-drafts/` 2. `/api/tasks/`
3. `/api/tasks/` 3. `/api/reminders/`
4. `/api/reminders/` 4. `/api/notifications/`
5. `/api/notifications/` 5. `/api/feedbacks/`
6. `/api/feedbacks/` 6. `/api/failure-records/`
7. `/api/failure-records/` 7. `/api/feishu/`
8. `/api/feishu/` 8. `/api/auth/feishu/`
9. `/api/auth/feishu/`
- [ ] **Step 2: 注册 Admin** - [ ] **Step 2: 注册 Admin**
@@ -593,9 +574,7 @@ Expected: 全部测试通过,Django check 无错误。
7. 定时提醒重复触发防护有测试。 7. 定时提醒重复触发防护有测试。
8. 通知失败、AI 解析失败、回调失败有失败记录。 8. 通知失败、AI 解析失败、回调失败有失败记录。
9. 普通日志不包含密钥、token、完整手机号或完整邮箱。 9. 普通日志不包含密钥、token、完整手机号或完整邮箱。
10. OAuth code/state、回调验签密钥、一次性操作 token 明文不进入普通日志或业务表 10. `python manage.py test``python manage.py check` 通过
11. 每个任务完成前说明新增或修改了哪些 tests、覆盖哪些 spec / contract 约定和如何运行。
12. `python manage.py test``python manage.py check` 通过。
## 执行备注 ## 执行备注
File diff suppressed because it is too large Load Diff
+6 -10
View File
@@ -6,8 +6,8 @@
## 2. 第一版做什么 ## 2. 第一版做什么
1. 事项可以来自 AI 草稿 `confirmed` 后转换 1. 事项可以来自 AI 草稿确认
2. 复杂事项来自 `route_type=manager_confirm_required`,老板确认后先创建 `pending_manager_confirm` 的事项壳 2. 事项可以由程经理确认复杂草稿后生成
3. 事项可以由老板或程经理手动创建。 3. 事项可以由老板或程经理手动创建。
4. 普通员工第一版不默认给别人创建事项。 4. 普通员工第一版不默认给别人创建事项。
5. 事项必须有发起人、接收人、事项内容、反馈要求和状态。 5. 事项必须有发起人、接收人、事项内容、反馈要求和状态。
@@ -36,7 +36,6 @@
```text ```text
AI 草稿或手动输入 AI 草稿或手动输入
-> 人工确认 -> 人工确认
-> AI 草稿进入 confirmed
-> 创建 tasks -> 创建 tasks
-> 创建 notifications -> 创建 notifications
-> 飞书通知接收人 -> 飞书通知接收人
@@ -49,11 +48,9 @@ AI 草稿或手动输入
```text ```text
老板确认转程经理 老板确认转程经理
-> AI 草稿进入 confirmed -> 创建 pending_manager_confirm 事项或待确认记录
-> 创建 pending_manager_confirm 事项壳,接收人和内容可由程经理补齐
-> 给程经理发飞书提醒 -> 给程经理发飞书提醒
-> 程经理进入平台确认、修改和分发 -> 程经理进入平台确认、修改和分发
-> 事项壳进入 pending_notify
-> 通知接收人 -> 通知接收人
-> 接收反馈 -> 接收反馈
``` ```
@@ -101,10 +98,9 @@ AI 草稿或手动输入
1. 未确认事项不得通知接收人。 1. 未确认事项不得通知接收人。
2. `pending_manager_confirm` 必须由程经理确认后才能进入通知流程。 2. `pending_manager_confirm` 必须由程经理确认后才能进入通知流程。
3. `manager_confirm_required` 不能绕过程经理直接通知最终接收人;必须先创建 `pending_manager_confirm` 事项壳 3. 通知失败进入 `notify_failed`,不得假装已通知
4. 通知失败进入 `notify_failed`,不得假装已通知 4. 已取消事项不能反馈、通知或补发
5. 已取消事项不能反馈、通知或补发 5. 有问题反馈会将事项标记为 `problem`,并记录原因
6. 有问题反馈会将事项标记为 `problem`,并记录原因。
## 9. 失败和日志 ## 9. 失败和日志
+11 -33
View File
@@ -16,8 +16,6 @@
8. 接收卡片按钮回调。 8. 接收卡片按钮回调。
9. 支持接收人反馈已收到、处理中、已完成、有问题。 9. 支持接收人反馈已收到、处理中、已完成、有问题。
10. 记录飞书事件、通知发送结果和回调处理结果。 10. 记录飞书事件、通知发送结果和回调处理结果。
11. 旧卡片、失效卡片和重复点击必须幂等处理。
12. 平台内“有问题反馈”页需要在飞书手机端最小可用。
## 3. 第一版不做 ## 3. 第一版不做
@@ -50,8 +48,8 @@
-> 记录 feishu_events -> 记录 feishu_events
-> 匹配平台用户 -> 匹配平台用户
-> 校验 boss 角色 -> 校验 boss 角色
-> 转发到 POST /api/secretary/handle-message -> 调用 AI 草稿服务
-> 根据 reply_type 发送确认卡片、追问或普通回复 -> 发送确认卡片
``` ```
卡片回调: 卡片回调:
@@ -61,9 +59,7 @@
-> 验签 -> 验签
-> 记录 feishu_events -> 记录 feishu_events
-> 查找 notification / draft / task / reminder -> 查找 notification / draft / task / reminder
-> 校验 event_id 或 idempotency_key
-> 校验操作人权限 -> 校验操作人权限
-> 校验通知、草稿和卡片仍有效
-> 执行业务动作 -> 执行业务动作
-> 写 operation_logs -> 写 operation_logs
-> 返回飞书处理结果 -> 返回飞书处理结果
@@ -80,11 +76,10 @@
5. `notifications` 5. `notifications`
6. `feedbacks` 6. `feedbacks`
7. `bot_contexts` 7. `bot_contexts`
8. `secretary_messages` 8. `failure_records`
9. `failure_records` 9. `operation_logs`
10. `operation_logs`
字段以 `docs/contracts/数据对象约定.md` 为准;权限、安全、日志脱敏、验签和幂等以 `docs/contracts/安全权限日志约定.md` 为准。 字段以 `docs/contracts/数据对象约定.md` 为准。
## 6. 接口需求 ## 6. 接口需求
@@ -112,13 +107,10 @@
状态以 `docs/contracts/状态流转约定.md` 为准。幂等约束: 状态以 `docs/contracts/状态流转约定.md` 为准。幂等约束:
1. 同一个飞书 `event_id` 只处理一次;没有稳定 `event_id` 时必须使用 `idempotency_key` 1. 同一个飞书 `event_id` 只处理一次。
2. 同一个通知只允许一个有效发送结果。 2. 同一个通知只允许一个有效发送结果。
3. 同一个卡片按钮重复点击不得重复创建事项、提醒或反馈。 3. 同一个卡片按钮重复点击不得重复创建事项、提醒或反馈。
4. 草稿确认类卡片必须匹配 `ai_drafts.active_card_notification_id` 4. 回调处理失败必须标记 `feishu_events.process_status = failed` 并写失败记录
5. 旧卡片、失效卡片、已替代草稿和已过期通知的回调只写入 `feishu_events`,标记 `ignored`,不得写业务对象。
6. 同一个机器人消息还必须按 `source + message_id` 在老板秘书入口二次幂等,不重复追加 `secretary_messages`
7. 回调处理失败必须标记 `feishu_events.process_status = failed` 并写失败记录。
## 9. 失败和日志 ## 9. 失败和日志
@@ -134,19 +126,9 @@
8. 操作人无权操作。 8. 操作人无权操作。
9. 有问题反馈缺少原因。 9. 有问题反馈缺少原因。
普通日志不得打印飞书 token、App Secret、OAuth code/state、一次性操作 token、回调验签密钥、完整手机号或完整邮箱。 普通日志不得打印飞书 token、App Secret、完整手机号或完整邮箱。
## 10. 平台内反馈页 ## 10. 给 AI 的测试样例
飞书卡片无法直接完成“有问题”原因填写时,允许跳转平台内反馈页。第一版不做完整手机网页适配,但该页在飞书手机端必须最小可用:
1. 能通过飞书登录态或一次性反馈 token 识别反馈人。
2. 一次性反馈 token 只保存 hash,不保存明文。
3. 只展示事项或提醒摘要、反馈状态选择和问题原因输入。
4. 操作人不是接收人、链接过期、通知失效或卡片失效时,只提示不可反馈并记录事件或失败,不写业务对象。
5. 提交成功后写入 `feedbacks` 并按状态流转更新事项或提醒。
## 11. 给 AI 的测试样例
1. 验签失败的回调应返回拒绝并写失败记录。 1. 验签失败的回调应返回拒绝并写失败记录。
2. 同一 `event_id` 重放应幂等,不重复创建反馈。 2. 同一 `event_id` 重放应幂等,不重复创建反馈。
@@ -154,15 +136,11 @@
4. 老板点击取消草稿后,草稿不能再转换。 4. 老板点击取消草稿后,草稿不能再转换。
5. 非老板私聊机器人不应调用 AI。 5. 非老板私聊机器人不应调用 AI。
6. 飞书通知失败时,应写 `failure_records` 并保留通知失败状态。 6. 飞书通知失败时,应写 `failure_records` 并保留通知失败状态。
7. 没有稳定 `event_id` 的卡片回调应使用 `idempotency_key` 幂等。
8. 旧确认卡片或已失效通知的回调只记录 `feishu_events`,不得创建事项、提醒或反馈。
9. 飞书手机端平台反馈页必须能提交“有问题”原因,并拒绝过期 token。
## 12. Review 重点 ## 11. Review 重点
1. 回调是否验签。 1. 回调是否验签。
2. 回调是否使用 `event_id``idempotency_key` 幂等。 2. 回调是否幂等。
3. 飞书身份是否没有直接绕过平台权限。 3. 飞书身份是否没有直接绕过平台权限。
4. 通知内容是否只发摘要和链接。 4. 通知内容是否只发摘要和链接。
5. 失败是否可复盘。 5. 失败是否可复盘。
6. 旧卡片、失效卡片是否不会继续写业务对象。
+2 -6
View File
@@ -9,7 +9,7 @@
1. 支持一次性提醒。 1. 支持一次性提醒。
2. 支持每天、每周、每月固定周期提醒。 2. 支持每天、每周、每月固定周期提醒。
3. 支持提醒来自手动创建。 3. 支持提醒来自手动创建。
4. 支持提醒来自老板 AI 草稿 `confirmed` 后转换 4. 支持提醒来自老板 AI 草稿确认
5. 支持提醒与事项关联。 5. 支持提醒与事项关联。
6. 到点后通知接收人。 6. 到点后通知接收人。
7. 通知成功或失败都要记录。 7. 通知成功或失败都要记录。
@@ -33,7 +33,6 @@
```text ```text
手动创建或草稿确认 手动创建或草稿确认
-> 后端校验创建权限 -> 后端校验创建权限
-> AI 提醒草稿必须有明确 scheduled_at
-> 写入 reminders -> 写入 reminders
-> 计算 next_trigger_at -> 计算 next_trigger_at
-> 写 operation_logs -> 写 operation_logs
@@ -44,7 +43,6 @@
```text ```text
scheduler 扫描 due reminders scheduler 扫描 due reminders
-> 校验提醒状态 -> 校验提醒状态
-> 用事务锁定当前批次
-> 生成幂等键 -> 生成幂等键
-> 创建 notifications -> 创建 notifications
-> 调用飞书发送 -> 调用飞书发送
@@ -108,10 +106,9 @@ scheduler 扫描 due reminders
2. `paused` 不触发,但可以恢复。 2. `paused` 不触发,但可以恢复。
3. `cancelled` 不触发、不可恢复。 3. `cancelled` 不触发、不可恢复。
4. `trigger_failed` 必须写失败记录。 4. `trigger_failed` 必须写失败记录。
5. 同一个提醒同一触发时间同一接收人同一渠道只能生成一条有效通知。 5. 同一个提醒同一触发时间同一接收人只能生成一条有效通知。
6. 一次性提醒成功触发后进入 `triggered`;周期提醒成功触发后保持 `active` 并计算下一次 `next_trigger_at` 6. 一次性提醒成功触发后进入 `triggered`;周期提醒成功触发后保持 `active` 并计算下一次 `next_trigger_at`
7. 提醒反馈不改变提醒调度状态,反馈结果通过 `feedbacks` 展示。 7. 提醒反馈不改变提醒调度状态,反馈结果通过 `feedbacks` 展示。
8. AI 提醒草稿缺少明确时间时不得创建 active 提醒,应由 01 模块追问或标记 `missing_fields`
## 9. 失败和日志 ## 9. 失败和日志
@@ -140,4 +137,3 @@ scheduler 扫描 due reminders
3. 是否不支持 cron 等第一版不做内容。 3. 是否不支持 cron 等第一版不做内容。
4. scheduler 是否走 service 层而不是直接改业务表。 4. scheduler 是否走 service 层而不是直接改业务表。
5. 失败是否可复盘。 5. 失败是否可复盘。
6. 是否使用事务锁和 `notifications.idempotency_key` 唯一约束防止重复触发。
+10 -22
View File
@@ -4,14 +4,12 @@
权限、日志和失败记录模块负责保证第一版闭环可控、可追溯、可复盘。权限必须在后端校验,关键操作必须写日志,AI、飞书、调度和业务失败必须形成失败记录。 权限、日志和失败记录模块负责保证第一版闭环可控、可追溯、可复盘。权限必须在后端校验,关键操作必须写日志,AI、飞书、调度和业务失败必须形成失败记录。
权限、安全、日志脱敏、飞书验签幂等和访问边界的全局事实源是 `docs/contracts/安全权限日志约定.md`;本 spec 只说明模块目标和业务补充。
## 2. 第一版做什么 ## 2. 第一版做什么
1. 定义老板、程经理、普通员工、管理员 / AI 团队四类角色。 1. 定义老板、程经理、普通员工、管理员 / AI 团队四类角色。
2. 控制事项、提醒、草稿、反馈、失败记录的可见范围和操作范围。 2. 控制事项、提醒、草稿、反馈、失败记录的可见范围和操作范围。
3. 记录关键操作日志。 3. 记录关键操作日志。
4. 记录 AI 解析失败、模型调用失败、AI 记忆读写失败、草稿转换失败、人员映射缺失、飞书通知失败、回调失败、定时触发失败、用户反馈有问题等失败。 4. 记录 AI 解析失败、人员映射缺失、飞书通知失败、回调失败、定时触发失败、用户反馈有问题等失败。
5. 支持管理员 / AI 团队查看和处理失败记录。 5. 支持管理员 / AI 团队查看和处理失败记录。
6. 对敏感信息做日志边界约束。 6. 对敏感信息做日志边界约束。
@@ -65,13 +63,9 @@
3. `operation_logs` 3. `operation_logs`
4. `model_call_logs` 4. `model_call_logs`
5. `feishu_events` 5. `feishu_events`
6. `secretary_messages`
7. `bot_contexts`
字段以 `docs/contracts/数据对象约定.md` 为准。 字段以 `docs/contracts/数据对象约定.md` 为准。
安全和访问边界以 `docs/contracts/安全权限日志约定.md` 为准。
## 6. 接口需求 ## 6. 接口需求
主要接口: 主要接口:
@@ -96,7 +90,6 @@
5. 普通员工不能给别人创建事项或提醒。 5. 普通员工不能给别人创建事项或提醒。
6. 反馈人必须是事项或提醒接收人。 6. 反馈人必须是事项或提醒接收人。
7. 飞书登录用户必须匹配到启用状态的平台用户后才能进入平台。 7. 飞书登录用户必须匹配到启用状态的平台用户后才能进入平台。
8. 模型调用日志、飞书原始 payload、失败记录原始错误和一次性操作 token 明文不得对普通员工开放。
## 8. 状态流转 ## 8. 状态流转
@@ -114,16 +107,15 @@
2. 机器人收到老板消息。 2. 机器人收到老板消息。
3. 非老板私聊机器人。 3. 非老板私聊机器人。
4. AI 生成草稿、解析失败。 4. AI 生成草稿、解析失败。
5. AI 追问、普通记录、上下文回顾和 BotContext 变化 5. 人工确认、修改、取消草稿
6. 人工确认、修改、取消草稿 6. 老板点击补充/重说
7. 老板点击补充/重说 7. 程经理确认复杂事项
8. 程经理确认复杂事项 8. 创建事项、创建提醒
9. 创建事项、创建提醒 9. 发送通知、补发通知、通知失败
10. 发送通知、补发通知、通知失败 10. 接收反馈,尤其是有问题反馈
11. 接收反馈,尤其是有问题反馈 11. 定时提醒触发成功或失败
12. 定时提醒触发成功或失败。 12. 失败记录处理
13. AI 记忆读写失败、草稿转换失败和失败记录处理 13. 背景摘要或老板风格提示词版本启用
14. 背景摘要或老板风格提示词版本启用。
## 10. 给 AI 的测试样例 ## 10. 给 AI 的测试样例
@@ -133,9 +125,6 @@
4. 管理员处理失败记录时,必须留下处理结果。 4. 管理员处理失败记录时,必须留下处理结果。
5. 日志中不得包含完整手机号、完整邮箱、飞书 token 或密钥。 5. 日志中不得包含完整手机号、完整邮箱、飞书 token 或密钥。
6. 未匹配平台用户的飞书登录应失败并写失败记录。 6. 未匹配平台用户的飞书登录应失败并写失败记录。
7. PostgreSQL AI 记忆读写失败时,应写 `memory_store_failed`,不得继续依赖内存确认草稿。
8. 飞书旧卡片或失效卡片回调只记录事件,不写业务对象。
9. 一次性操作 token 只能保存 hash,普通日志不得出现明文。
## 11. Review 重点 ## 11. Review 重点
@@ -144,4 +133,3 @@
3. 失败记录是否包含关联对象、失败类型和处理结果。 3. 失败记录是否包含关联对象、失败类型和处理结果。
4. 日志是否记录操作人和渠道。 4. 日志是否记录操作人和渠道。
5. 敏感信息是否被脱敏。 5. 敏感信息是否被脱敏。
6. 访问范围是否符合 `安全权限日志约定.md`
-5
View File
@@ -1,5 +0,0 @@
# tests
`tests/` 是真实约束目录。Django 项目骨架落地前,本目录只做占位;后续测试代码按 Django 默认测试或 pytest 约定放入这里或各 app 的 `tests/` 中。
每个 active plan 完成前必须说明新增或修改了哪些测试、覆盖了哪些 spec / contract 约定、如何运行,以及还有哪些关键风险未覆盖。