补充安全权限日志与草稿状态约定

This commit is contained in:
talesofzes
2026-06-22 17:30:59 +08:00
parent df0b3fa267
commit a41e2c28d4
19 changed files with 451 additions and 129 deletions
+9 -2
View File
@@ -11,6 +11,7 @@
5. 动作类接口使用 `POST /api/{resources}/{id}/{action}`
6. 飞书回调接口必须单独记录原始 payload。
7. 后端必须做权限校验,不能只靠前端隐藏入口。
8. 涉及权限、安全、日志脱敏、飞书验签和幂等时,以 `docs/contracts/安全权限日志约定.md` 为准。
## 2. 统一响应格式
@@ -66,6 +67,7 @@
| `not_found` | 404 | 对象不存在或不可见 |
| `state_conflict` | 409 | 当前状态不允许该操作 |
| `idempotency_conflict` | 409 | 幂等键冲突或重复请求 |
| `card_expired` | 409 | 飞书卡片、通知或一次性操作链接已失效 |
| `ai_parse_failed` | 422 | AI 输出无法解析或不符合结构 |
| `ai_model_failed` | 502 | 百炼或模型网关失败,重试后仍不可用 |
| `missing_person_mapping` | 422 | 缺少人员映射 |
@@ -146,6 +148,8 @@
- `unsupported`
- `error`
`note` / `unknown` / `qa` / `realtime_qa` / `need_more_info` / `unsupported` 不得创建事项、提醒或通知;如需留痕,只返回回复、追问、上下文摘要或失败信息。
兼容接口 `/api/ai-drafts/parse``/api/demo/boss-message` 可以保留,但内部必须转发到 `/api/secretary/handle-message`
## 7. 事项
@@ -200,9 +204,11 @@
1. 必须验签。
2. 必须记录原始 payload 到 `feishu_events`
3. 必须使用 `event_id` 幂等。
3. 必须使用 `event_id` 幂等;没有稳定 `event_id` 时必须使用 `idempotency_key`
4. 验签失败不得执行业务动作。
5. 回调处理失败必须写入 `failure_records`
5. 草稿确认类卡片必须校验 `ai_drafts.active_card_notification_id`
6. 旧卡片、失效卡片、已替代草稿和已过期通知的回调只记录 `feishu_events`,不写业务对象。
7. 回调处理失败必须写入 `failure_records`
## 11. 敏感信息返回边界
@@ -210,3 +216,4 @@
2. 手机号和邮箱默认脱敏返回,除非管理员维护页确有需要。
3. 飞书通知只发送摘要和链接,不发送敏感全文。
4. 模型调用日志对外接口默认不返回完整 prompt 和原始大段上下文。
5. 飞书原始 payload、模型原始请求/响应、OAuth code/state、一次性操作 token 和回调验签密钥的访问边界以 `安全权限日志约定.md` 为准。
+136
View File
@@ -0,0 +1,136 @@
# 安全权限日志约定
本文件是第一版安全、权限、日志脱敏、回调验签、幂等和访问边界的唯一事实源。涉及这些规则时,代码、接口、测试和 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. 不把飞书机器人开放为程经理或普通员工通用派活入口。
+75 -16
View File
@@ -50,6 +50,7 @@
| `aliases` | JSON 数组,例如 `["东东", "CJN"]` |
| `department` | 部门 |
| `business_role` | 业务角色,例如行政、程经理、下单员 |
| `manager_user_id` | 第一批试用管理范围归属,可选 |
| `phone` | 手机号,可选 |
| `email` | 邮箱,可选 |
| `feishu_open_id` | 飞书 open_id |
@@ -59,6 +60,13 @@
| `is_trial_user` | 是否第一批试用人员 |
| `note` | 备注 |
口径:
1. `users` 是登录、角色和权限判断的权威对象。
2. `person_mappings` 是自然语言称呼、手机号 / 邮箱、飞书身份和第一批试用人员映射的权威对象。
3. 同一个真实人员如果可以登录平台,应在 `person_mappings.user_id` 绑定对应 `users.id`
4. 程经理第一批试用管理范围先用 `manager_user_id` 或等价白名单表达,不引入完整组织树。
## 4. feishu_auth_sessions
记录飞书登录过程和平台用户绑定结果。
@@ -103,7 +111,7 @@
| `role` | `boss``assistant``system` |
| `text` | 原始输入或 AI 回复 |
| `answer` | AI 给老板的回复,可为空 |
| `intent_type` | `task``reminder``qa``realtime_qa``note``need_more_info``unsupported``context_summary` |
| `intent_type` | `task``reminder``qa``realtime_qa``note``need_more_info``unknown``unsupported``context_summary` |
| `draft_id` | 关联 AI 草稿,可为空 |
| `raw_payload` | 原始请求摘要,避免保存密钥 |
| `bot_context_snapshot` | JSONB,消息发生时的上下文快照 |
@@ -118,7 +126,7 @@
| `id` | 上下文 ID |
| `conversation_id` | 当前老板秘书会话 ID |
| `boss_id` | PostgreSQL 中的老板用户 ID |
| `status` | `empty``awaiting_more_info``awaiting_confirm``expired``cleared` |
| `status` | `empty``awaiting_more_info``awaiting_confirm``awaiting_follow_up``expired``cleared` |
| `pending_draft_id` | 当前待补充或待确认的草稿 ID |
| `pending_draft_type` | `task``reminder``none` |
| `last_intent` | 上一条有效意图 |
@@ -128,6 +136,8 @@
| `extracted_facts` | JSONB,从多轮对话提取的候选事实,只用于草稿修订 |
| `updated_at` | 上下文最近更新时间 |
`awaiting_follow_up` 专用于老板点击飞书确认卡片上的“补充/重说”后,等待下一条消息修订上一版草稿;该状态必须带 `pending_draft_id``expires_at`
## 8. prompt_contexts
维护 AI 秘书调用模型时加载的上下文版本。
@@ -153,26 +163,42 @@
| --- | --- |
| `id` | 草稿 ID |
| `source` | `platform``feishu_bot` |
| `source_message_id` | 飞书或外部入口消息 ID,可选 |
| `created_by` | 发起人 |
| `parent_draft_id` | 补充/重说生成新草稿时,关联上一版草稿 |
| `raw_input` | 原始输入 |
| `intent` | `task``reminder` |
| `draft_type` | `task``reminder` |
| `should_create_draft` | 固定为 true`qa/realtime_qa/note/need_more_info/unsupported` 不落 ai_drafts |
| `intent` | `task``reminder``qa``realtime_qa``note``need_more_info``unknown``unsupported` |
| `draft_type` | `task``reminder``none` |
| `should_create_draft` | `task/reminder` 为 true;其他意图必须为 false |
| `status` | 见状态流转约定 |
| `title` | 草稿标题 |
| `content` | 事项或提醒内容 |
| `receiver_candidates` | JSON,接收人候选和置信度 |
| `receiver_text` | 未映射成功时保留老板原始称呼 |
| `selected_receiver_id` | 人工确认后的接收人,可选 |
| `scheduled_at` | 提醒时间,可为空;任务可使用 `schedule_text` 表达期望时间 |
| `schedule_text` | 老板原始时间表达 |
| `recurrence_type` | `none``daily``weekly``monthly` |
| `requires_feedback` | 是否需要反馈 |
| `route_type` | `direct_after_boss_confirm``manager_confirm_required` |
| `route_type` | `none``direct_after_boss_confirm``manager_confirm_required` |
| `need_manager_confirm` | 是否需要程经理确认 |
| `missing_fields` | JSON 数组 |
| `questions` | JSON 数组,最多 3 个;确认草稿一般为空 |
| `answer` | 给老板看的回复摘要;不得写“已通知、已创建、已发送”等执行语义 |
| `active_card_notification_id` | 当前有效确认卡片通知,可选 |
| `superseded_by_draft_id` | 被补充/重说的新草稿替代时,指向新草稿 |
| `model_call_log_id` | 关联模型调用日志 |
| `confirmed_by` | 确认人 |
| `confirmed_at` | 确认时间 |
| `cancelled_reason` | 取消原因 |
草稿生命周期口径:
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
@@ -183,12 +209,17 @@
| `id` | 日志 ID |
| `provider` | `bailian` |
| `model_name` | 模型名 |
| `prompt_context_snapshot` | 本次使用的上下文版本和摘要 |
| `prompt_version` | 提示词版本 |
| `background_context_version` | 公司背景摘要版本 |
| `boss_style_version` | 老板沟通风格版本 |
| `ai_secretary_rules_version` | AI 秘书业务规则版本 |
| `context_snapshot` | 本次使用的上下文版本和摘要 |
| `input_text` | 用户原始输入 |
| `request_payload` | 请求摘要,避免保存密钥 |
| `raw_response` | 模型原始响应 |
| `parsed_json` | 解析后的 `AiSecretaryResponse` JSON |
| `response_payload` | 模型原始响应结构 |
| `parsed_result` | 后端解析后的 `AiSecretaryResponse` JSON |
| `status` | `success``failed` |
| `failure_reason` | 失败原因 |
| `error_message` | 失败原因 |
| `latency_ms` | 耗时 |
| `created_by` | 调用发起人 |
@@ -199,11 +230,12 @@
| 字段 | 说明 |
| --- | --- |
| `id` | 事项 ID |
| `source_type` | `ai_draft``manual` |
| `source_draft_id` | 来源草稿,可选 |
| `title` | 标题 |
| `content` | 事项内容 |
| `initiator_id` | 发起人 |
| `receiver_id` | 接收人 |
| `receiver_id` | 接收人;复杂事项在程经理确认前可为空 |
| `manager_id` | 复杂事项确认人,可选 |
| `status` | 见状态流转约定 |
| `visible_feedback_status` | `received``in_progress``completed``problem` |
@@ -214,6 +246,8 @@
| `completed_at` | 完成时间 |
| `problem_reason` | 有问题原因,可选 |
复杂事项必须在老板确认后创建 `status=pending_manager_confirm` 的事项壳,不得一直停留在草稿中等待程经理补齐。
## 12. reminders
未来某个时间触发的提醒。
@@ -221,6 +255,7 @@
| 字段 | 说明 |
| --- | --- |
| `id` | 提醒 ID |
| `source_type` | `ai_draft``manual` |
| `source_draft_id` | 来源草稿,可选 |
| `related_task_id` | 关联事项,可选 |
| `title` | 标题 |
@@ -244,16 +279,33 @@
| `id` | 通知 ID |
| `target_type` | `ai_draft``task``reminder``failure_record` |
| `target_id` | 关联对象 ID |
| `purpose` | `draft_confirm``manager_confirm``task_notify``reminder_trigger` |
| `receiver_id` | 接收人 |
| `channel` | `feishu``platform` |
| `channel` | `feishu_personal``feishu_group``platform` |
| `message_type` | `text``card` |
| `title` | 通知标题 |
| `summary` | 通知摘要 |
| `link_url` | 平台详情链接 |
| `status` | `pending``sent``failed``retrying``cancelled` |
| `idempotency_key` | 幂等键 |
| `card_id` | 飞书卡片 ID,可选 |
| `status` | `pending``sending``sent``failed``retrying``cancelled``expired` |
| `trigger_time` | 提醒触发时间;非提醒触发通知可为空 |
| `idempotency_key` | 幂等键,按通知目的生成并唯一约束 |
| `action_token_hash` | 平台反馈页一次性操作 token 的 hash,可选 |
| `feishu_message_id` | 飞书消息 ID |
| `sent_at` | 发送时间 |
| `expires_at` | 卡片或通知操作过期时间,可选 |
| `invalidated_at` | 被新卡片替代或业务取消时的失效时间,可选 |
| `failure_reason` | 失败原因 |
| `retry_count` | 重试次数 |
| `last_retry_at` | 最近重试时间 |
提醒触发类通知的幂等键格式建议:
```text
reminder:{reminder_id}:{trigger_time}:{receiver_id}:{channel}
```
任务通知、草稿确认卡片和程经理待确认提醒也必须生成各自的幂等键,避免重复点击、重复补发或回调重放造成多条有效通知。
## 14. feedbacks
@@ -289,6 +341,8 @@ AI、通知、回调、调度等失败原因。
| `handled_at` | 处理时间 |
| `handle_result` | 处理结果 |
`target_type` 可使用 `ai_draft``task``reminder``notification``feishu_event``model_call_log` 等实际对象类型。AI 解析失败时,如果无法创建最小 `parse_failed` 草稿,可关联 `model_call_log`
## 16. operation_logs
人工确认、修改、取消、补发等过程留痕。
@@ -317,6 +371,7 @@ AI、通知、回调、调度等失败原因。
| `event_type` | 事件类型 |
| `operator_open_id` | 操作人 open_id |
| `action_value` | 按钮值 |
| `idempotency_key` | 事件处理幂等键,可选 |
| `target_type` | `ai_draft``task``reminder``auth``bot` |
| `target_id` | 关联业务对象 |
| `notification_id` | 关联通知 |
@@ -325,9 +380,13 @@ AI、通知、回调、调度等失败原因。
| `process_result` | 处理结果 |
| `created_at` | 接收时间 |
`event_id` 应尽量设置唯一约束;如果飞书某类回调没有稳定 `event_id`,则用 `operator_open_id + action_value + notification_id + target_id` 生成 `idempotency_key` 并唯一约束。重复事件只返回已处理结果,不重复创建草稿、事项、提醒、反馈或通知。
## 18. 敏感字段规则
1. `phone``email` 可入库,但普通日志和普通列表接口默认脱敏。
2. API Key、App Secret、飞书 token 不得入库到业务表或普通日志。
3. `raw_error``metadata``request_payload` 必须避免密钥和完整个人联系方式。
2. API Key、App Secret、飞书 token、飞书回调验签密钥、OAuth code/state、一次性操作 token 明文不得入库到业务表或普通日志。
3. `raw_error``metadata``request_payload``response_payload` 必须避免密钥和完整个人联系方式。
4. 飞书通知内容只保存和发送摘要,不展开敏感全文。
5. `model_call_logs` 原始请求/响应和 `feishu_events.raw_payload` 仅供管理员 / AI 团队排查,普通员工不可见,老板和程经理只在必要业务范围内查看摘要。
6. 平台反馈页一次性操作 token 只能保存 hash,不能保存明文。
+41 -19
View File
@@ -14,31 +14,41 @@
| 状态 | 含义 |
| --- | --- |
| `PENDING_CONFIRM` | 草稿已整理完成,等待老板确认、修改、取消或转经理 |
| `NEED_MANAGER_CONFIRM` | 老板已确认,但复杂任务需要程经理确认 |
| `CONFIRMED` | 已人工确认,等待转换任务或提醒 |
| `CONVERTED` | 已转换为事项或提醒 |
| `CANCELLED` | 已取消 |
| `FAILED` | AI 解析或草稿处理失败,仅用于调试追踪 |
| `pending_confirmation` | 草稿已整理完成,等待老板确认、修改、取消或补充/重说 |
| `awaiting_follow_up` | 老板已点击补充/重说,等待 30 分钟内下一条补充消息 |
| `confirmed` | 已人工确认,等待转换为事项或提醒 |
| `converted` | 已转换为事项或提醒;复杂事项已创建 `pending_manager_confirm` 事项壳也视为已转换 |
| `cancelled` | 已取消 |
| `answered` | 已直接回复或留痕,不进入事项、提醒或通知闭环 |
| `superseded` | 已被补充/重说生成的新草稿替代 |
| `expired` | 补充/重说等待超时,旧确认卡片不可继续使用 |
| `parse_failed` | AI 解析或草稿处理失败,仅用于调试追踪 |
允许流转:
```text
PENDING_CONFIRM -> CONFIRMED
PENDING_CONFIRM -> NEED_MANAGER_CONFIRM
PENDING_CONFIRM -> CANCELLED
PENDING_CONFIRM -> FAILED
NEED_MANAGER_CONFIRM -> CONFIRMED
CONFIRMED -> CONVERTED
CONFIRMED -> FAILED
pending_confirmation -> confirmed
pending_confirmation -> awaiting_follow_up
pending_confirmation -> cancelled
pending_confirmation -> parse_failed
awaiting_follow_up -> superseded
awaiting_follow_up -> expired
awaiting_follow_up -> cancelled
confirmed -> converted
confirmed -> parse_failed
```
约束:
1. `FAILED``CANCELLED``CONVERTED` 为终态。
2. 未进入 `CONFIRMED` 的草稿不得转换。
3. `need_more_info``answered``context_summary` 是非草稿处理结果,不落 `ai_drafts.status`
4. 多次补充/重说优先合并到同一个 `pending_draft`,直到老板确认、取消、过期或明确新建,不要求每次补充都新建草稿
1. `parse_failed``cancelled``converted``answered``superseded``expired` 为终态。
2. 未进入 `confirmed` 的草稿不得转换。
3. `note``unknown``qa``realtime_qa``need_more_info``unsupported` 不进入事项、提醒或通知闭环;如需留痕,只能使用 `answered``parse_failed`
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. 事项状态
@@ -144,21 +154,28 @@ triggered -> expired
| 状态 | 含义 |
| --- | --- |
| `pending` | 待发送 |
| `sending` | 发送中 |
| `sent` | 已发送 |
| `failed` | 发送失败 |
| `retrying` | 补发中 |
| `cancelled` | 已取消 |
| `expired` | 卡片或通知操作已过期 |
允许流转:
```text
pending -> sending
pending -> sent
pending -> failed
sending -> sent
sending -> failed
failed -> retrying
retrying -> sent
retrying -> failed
pending -> cancelled
failed -> cancelled
pending -> expired
sent -> expired
```
约束:
@@ -166,6 +183,8 @@ failed -> cancelled
1. `sent` 不得重复发送同一通知。
2. 补发必须保留原失败记录和新发送结果。
3. 飞书通知必须使用幂等键。
4. `expired``cancelled` 通知不得继续处理卡片确认、反馈或补充/重说。
5. 被新卡片替代或业务取消时,必须写 `invalidated_at`
通知幂等键建议:
@@ -252,8 +271,10 @@ processing -> cancelled
约束:
1. 同一 `event_id` 只处理一次。
2. 验签失败不得执行业务动作
3. 处理失败必须写失败记录
2. 如果飞书某类回调没有稳定 `event_id`,必须使用 `idempotency_key` 做事件幂等
3. 验签失败不得执行业务动作
4. 旧卡片、失效卡片、已替代草稿和已过期通知的回调只记录事件并标记 `ignored`,不得写业务对象。
5. 处理失败必须写失败记录。
## 12. 用户和配置状态
@@ -295,5 +316,6 @@ Prompt 上下文状态:
| `empty` | 当前没有可补充或可确认的草稿 |
| `awaiting_more_info` | AI 已追问,等待老板补充 |
| `awaiting_confirm` | 草稿已整理完成,等待老板确认、修改、取消或转经理 |
| `awaiting_follow_up` | 老板点击补充/重说后,等待下一条消息修订上一版草稿 |
| `expired` | 已过期 |
| `cleared` | 草稿已确认、取消或转换完成,上下文已清空 |