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

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
+67 -51
View File
@@ -15,7 +15,7 @@
```text
老板一句话
-> AiSecretaryAgent 判断意图和上下文
-> 意图归类为 task / reminder / qa / realtime_qa / note / need_more_info / unsupported
-> 意图归类为 task / reminder / qa / realtime_qa / note / need_more_info / unknown / unsupported
-> 稳定 AiSecretaryResponse JSON
-> 生成 AI 草稿 / 追问 / 直接回复
-> 老板确认、补充、取消或转程经理
@@ -23,7 +23,7 @@
- 支持老板通过飞书私聊或平台入口输入文本。
- 统一走 `POST /api/secretary/handle-message`
- 判断 `task/reminder/qa/realtime_qa/note/need_more_info/unsupported`
- 判断 `task/reminder/qa/realtime_qa/note/need_more_info/unknown/unsupported`
- 生成稳定的 `AiSecretaryResponse` JSON。
- 对任务和提醒只生成可确认草稿,不直接执行。
- 对普通聊天、实时问答兜底、普通记录直接回复或记录。
@@ -62,7 +62,7 @@ POST /api/secretary/handle-message
-> 校验发送人角色
-> 非老板直接固定回复并记录
-> 读取 BotContext,外部 context 只作为参考
-> 判断本次消息是 follow_up / new_request / qa / realtime_qa / note / unsupported / command
-> 判断本次消息是 follow_up / new_request / qa / realtime_qa / note / unknown / unsupported / command
-> 组装 PromptContext
-> 调用模型
-> 校验 AiSecretaryResponse
@@ -105,7 +105,7 @@ cleared
- 每次进入 `awaiting_more_info``awaiting_confirm` 状态时重置 `expires_at = now + 30 分钟`
- 点击或说出“补充/重说”后,30 分钟内下一条消息优先尝试作为上一条草稿的修正。
- 30 分钟内不能无条件吞掉所有消息,必须先判断为 `follow_up / new_request / qa / realtime_qa / note / unsupported / command`
- 30 分钟内不能无条件吞掉所有消息,必须先判断为 `follow_up / new_request / qa / realtime_qa / note / unknown / unsupported / command`
- 老板说“换成东东”“改到明天”“刚才那个不要了”,优先修正当前草稿。
- 老板说“另外再安排一个”“新建一个”“再帮我记一个”,创建新草稿。
- 老板说“确认”“就这样”“发给他”,确认当前草稿。
@@ -113,7 +113,7 @@ cleared
- 老板问“刚才我说了什么”“总结一下”,做上下文回顾,不修改草稿。
- 超过 30 分钟,上下文标记 `expired`,下一条默认按新输入处理;如果老板明显仍在说上一条草稿,可先提示“上一条草稿已过期,我按新请求处理”。
- 老板确认、取消或草稿转换为任务/提醒后立即清空上下文,不受 30 分钟窗口约束。
- 30 分钟窗口只绑定未确认草稿;草稿进入 `CONFIRMED / CONVERTED / CANCELLED` 后,后续“改一下刚才那个”不能再修改原草稿,只能作为已发布任务/提醒变更请求或第一阶段能力外请求处理。
- 30 分钟窗口只绑定未确认草稿;草稿进入 `confirmed / converted / cancelled / superseded / expired` 后,后续“改一下刚才那个”不能再修改原草稿,只能作为已发布任务/提醒变更请求或第一阶段能力外请求处理。
- 多次 `follow_up` 必须合并到同一个 `pending_draft`,直到老板确认、取消、过期或明确新建,不得每次补充都新建草稿。
- `BotContext` 只提供上下文候选,不强制绑定消息。当本地规则或模型无法判断是修改当前草稿、新建请求、普通聊天还是取消当前草稿时,必须走 `need_more_info` 追问,不允许默认修改当前草稿。
- 老板可以通过“另外、重新来、换个事、先别管这个、取消、重新安排”等自然语言随时跳出当前上下文。
@@ -143,7 +143,7 @@ cleared
- 3 轮仍补不全,也生成带 `missing_fields` 的草稿,不继续拉扯。
- 每次追问都必须保存一条 `SecretaryMessage``intent_type=need_more_info`,内容为本次给老板的问题。
- 追问轮次写入 `BotContext.follow_up_count`,并记录 `OperationLog(action=ask_follow_up)`
- `follow_up_count` 在以下情况重置为 0:草稿被确认、取消、过期、进入 `PENDING_CONFIRM`,或生成带 `missing_fields` 的终态草稿后收到下一条新消息。
- `follow_up_count` 在以下情况重置为 0:草稿被确认、取消、过期、进入 `pending_confirmation`,或生成带 `missing_fields` 的终态草稿后收到下一条新消息。
## 5. 数据对象
@@ -224,7 +224,7 @@ PostgreSQL 最小保存三类 AI 记忆数据:
"message_id": "外部消息ID或内部生成ID",
"role": "boss|assistant|system",
"text": "原始输入或AI回复",
"intent": "task|reminder|qa|realtime_qa|note|need_more_info|unsupported|context_summary",
"intent": "task|reminder|qa|realtime_qa|note|need_more_info|unknown|unsupported|context_summary",
"draft_id": "postgres_ai_draft_id",
"bot_context_snapshot": {},
"created_at": "2026-06-22T10:00:00+08:00"
@@ -246,7 +246,7 @@ PostgreSQL 最小保存三类 AI 记忆数据:
```json
{
"intent": "task|reminder|qa|realtime_qa|note|need_more_info|unsupported",
"intent": "task|reminder|qa|realtime_qa|note|need_more_info|unknown|unsupported",
"draft_type": "task|reminder|none",
"should_create_draft": true,
"route_type": "none|direct_after_boss_confirm|manager_confirm_required",
@@ -278,9 +278,9 @@ PostgreSQL 最小保存三类 AI 记忆数据:
校验规则:
- `task/reminder` 必须 `should_create_draft=true``draft_type` 必须对应为 `task``reminder`
- `qa/realtime_qa/note/need_more_info/unsupported` 必须 `should_create_draft=false``draft_type=none`
- `qa/realtime_qa/note/need_more_info/unknown/unsupported` 必须 `should_create_draft=false``draft_type=none`
- `task``route_type` 必填,只能是 `direct_after_boss_confirm``manager_confirm_required`
- `reminder/qa/realtime_qa/note/need_more_info/unsupported``route_type=none`
- `reminder/qa/realtime_qa/note/need_more_info/unknown/unsupported``route_type=none`
- `route_type=manager_confirm_required` 时,`draft.need_manager_confirm=true`;老板主动说“给程经理看看”“让经理确认”时,也必须置为 true。
- `route_type=direct_after_boss_confirm` 时,`draft.need_manager_confirm=false`
- `task` 必须有事项内容;时间不是必填项。
@@ -314,6 +314,7 @@ PostgreSQL 最小保存三类 AI 记忆数据:
| realtime_qa | 固定提示需要实时数据查询,第一版暂不作为正式能力 |
| note | 只保存消息记录,不生成草稿、不通知,可用于“刚才说了什么”回顾 |
| need_more_info | 信息不足但属于可处理范围,主动追问 |
| unknown | 暂时无法判断意图,只允许澄清或留痕 |
| unsupported | 超出第一版能力,解释为什么不能做,不追问执行字段 |
优先级和兜底规则:
@@ -322,6 +323,7 @@ PostgreSQL 最小保存三类 AI 记忆数据:
- `realtime_qa.answer` 固定使用模板:“这个需要实时数据查询,第一版暂不作为正式能力;我不会把它生成任务或提醒。”
- 超出第一版能力但不属于实时数据类的输入,归为 `unsupported`
- `unsupported.answer` 应说明当前入口只处理老板事项草稿、提醒草稿、普通问答和普通记录,不追问执行字段。
- `unknown.answer` 只能澄清或提示补充,不得创建事项、提醒或通知。
示例:
@@ -332,37 +334,51 @@ PostgreSQL 最小保存三类 AI 记忆数据:
### 5.4 草稿状态
`ai_drafts.status` 只表示已经落 PostgreSQL 草稿表的草稿状态。非草稿意图(`qa/realtime_qa/note/unsupported`)和纯追问结果(`need_more_info`)不`ai_drafts`,只保存 `SecretaryMessage``BotContext` 和处理结果
`ai_drafts.status` 只表示已经落 PostgreSQL 草稿表的草稿状态。非执行意图(`qa/realtime_qa/note/unknown/unsupported`)和纯追问结果(`need_more_info`)不得进入事项、提醒或通知闭环;如需留痕,只保存 `SecretaryMessage``BotContext``model_call_logs` 或最小草稿记录
`ai_drafts.status` 建议状态:
`ai_drafts.status` `docs/contracts/状态流转约定.md` 为准,核心状态:
```text
PENDING_CONFIRM
NEED_MANAGER_CONFIRM
CONFIRMED
CANCELLED
FAILED
CONVERTED
pending_confirmation
awaiting_follow_up
confirmed
converted
cancelled
answered
superseded
expired
parse_failed
```
对老板可见时可以简化为:待确认、待程经理确认、已确认、已取消、已失败。
对老板可见时可以简化为:待确认、等待补充、已确认、已取消、已失效、已完成、已失败。
状态含义:
- `PENDING_CONFIRM`:草稿可确认,等待老板确认。
- `NEED_MANAGER_CONFIRM`:老板已确认,但该任务仍需要程经理确认,不能直接转换任务
- `CONFIRMED`:确认链路已完成,可以交给任务或提醒模块转换。
- `CANCELLED`:老板取消草稿
- `FAILED`:草稿或 AI 解析过程失败的草稿状态;失败原因写入 `FailureRecord.failure_type`,例如 `ai_parse_failed`,两者不能混用
- `CONVERTED`:草稿已经成功转换为任务或提醒。
- `pending_confirmation`:草稿可确认,等待老板确认、补充/重说或取消
- `awaiting_follow_up`:老板点击补充/重说后,等待 30 分钟内下一条消息
- `confirmed`:确认链路已完成,可以交给任务或提醒模块转换。
- `converted`:草稿已经成功转换为任务或提醒;复杂事项已创建 `pending_manager_confirm` 事项壳也视为转换完成
- `cancelled`:老板取消草稿
- `answered`:已直接回复或留痕,不转换为任务或提醒。
- `superseded`:已被补充/重说生成的新草稿替代。
- `expired`:补充/重说等待超时,旧确认卡片不可继续使用。
- `parse_failed`:草稿或 AI 解析过程失败,仅用于调试追踪。
非草稿处理状态:
- `need_more_info`:本轮只追问老板,不创建 `ai_drafts`;追问内容保存为 `SecretaryMessage(intent_type=need_more_info)`,上下文状态为 `BotContext.status=awaiting_more_info`
- `answered``qa/realtime_qa/note/context_summary/unsupported` 的处理结果,不转换为任务或提醒,不影响当前有效 `BotContext`
- 追问 3 轮仍补不全、但系统决定生成带 `missing_fields` 的草稿时,模型输出必须从 `need_more_info` 转为 `task``reminder`,创建 `PENDING_CONFIRM` 草稿,并由确认阻塞规则控制能否确认。
- `answered``qa/realtime_qa/note/context_summary/unknown/unsupported` 的处理结果,不转换为任务或提醒,不影响当前有效 `BotContext`
- 追问 3 轮仍补不全、但系统决定生成带 `missing_fields` 的草稿时,模型输出必须从 `need_more_info` 转为 `task``reminder`,创建 `pending_confirmation` 草稿,并由确认阻塞规则控制能否确认。
`DRAFTING` 不作为第一版 `ai_drafts.status` 持久化枚举。实现中如需表达“AI 已决定生成草稿但还在校验/解析”的过程态,只能作为方法内的内存标记或局部变量;落库状态必须`PENDING_CONFIRM``NEED_MANAGER_CONFIRM``FAILED` 开始
`DRAFTING` 不作为第一版 `ai_drafts.status` 持久化枚举。实现中如需表达“AI 已决定生成草稿但还在校验/解析”的过程态,只能作为方法内的内存标记或局部变量;落库状态必须来自 `状态流转约定.md`
草稿确认卡片生命周期:
- 每个草稿同一时间只能有一张有效确认卡片,对应 `active_card_notification_id`
- 老板点击“补充/重说”后,原草稿进入 `awaiting_follow_up`,原确认卡片应失效。
- 30 分钟内收到补充消息后,新草稿 `parent_draft_id` 指向原草稿,原草稿进入 `superseded`
- 超过 30 分钟仍未收到补充消息时,原草稿进入 `expired`,老板下一条消息按新输入处理。
- 飞书回调处理确认、取消、补充/重说前,必须校验回调来自当前有效卡片;旧卡片只记录事件,不写业务对象。
### 5.5 Prompt 硬规则
@@ -467,25 +483,25 @@ POST /api/secretary/handle-message
-> 老板补充
-> 新草稿或修订草稿
-> 老板确认
-> direct_after_boss_confirm: CONFIRMED -> TaskAdapter / ReminderService 转换 -> CONVERTED
-> manager_confirm_required: NEED_MANAGER_CONFIRM -> 程经理确认 -> CONFIRMED -> TaskAdapter 转换 -> CONVERTED
-> direct_after_boss_confirm: confirmed -> TaskAdapter / ReminderService 转换 -> converted
-> manager_confirm_required: confirmed -> 创建 pending_manager_confirm 事项壳 -> converted -> 程经理确认后通知接收人
```
草稿确认后的边界:
- `direct_after_boss_confirm`:老板确认成功后,草稿进入 `CONFIRMED`
- `manager_confirm_required`:老板确认成功后,草稿进入 `NEED_MANAGER_CONFIRM`,通知或展示给程经理确认;程经理确认后才进入 `CONFIRMED`
- 任务/提醒转换成功后,草稿进入 `CONVERTED`,并清空 `BotContext`
- 任务/提醒转换失败时,不回滚老板确认;草稿保持 `CONFIRMED`,写 `FailureRecord(draft_convert_failed)`,支持后台重试或人工处理。
- 老板确认后,无论进入 `CONFIRMED` 还是 `NEED_MANAGER_CONFIRM`,都要清空老板侧 `BotContext`;后续经理确认不再依赖老板 30 分钟上下文。
- `NEED_MANAGER_CONFIRM / CONFIRMED / CONVERTED` 后,01 不再直接修改原草稿;老板后续说“把刚才发出去那个改一下”,应识别为任务/提醒变更请求,第一阶段未接入变更能力时回复暂不支持或引导到任务模块。
- `direct_after_boss_confirm`:老板确认成功后,草稿进入 `confirmed`,转换成功后进入 `converted`
- `manager_confirm_required`:老板确认成功后,草稿进入 `confirmed`;转换时先创建 `tasks.status=pending_manager_confirm` 的事项壳,并给程经理发送待确认提醒;事项壳创建成功后草稿进入 `converted`
- 程经理确认复杂事项时,补齐接收人、内容和通知信息,使事项进入 `pending_notify`,再通知接收人
- 任务/提醒转换失败时,不回滚老板确认;草稿保持 `confirmed`,写 `FailureRecord(draft_convert_failed)`,支持后台重试或人工处理。
- 老板确认后要清空老板侧 `BotContext`;后续经理确认不再依赖老板 30 分钟上下文。
- `confirmed / converted / cancelled / superseded / expired` 后,01 不再直接修改原草稿;老板后续说“把刚才发出去那个改一下”,应识别为任务/提醒变更请求,第一阶段未接入变更能力时回复暂不支持或引导到任务模块。
直接回复流转:
```text
qa / realtime_qa / note / context_summary
qa / realtime_qa / note / unknown / context_summary
-> answered 处理结果
-> 不落 ai_drafts,不转换,不清空当前有效 BotContext
-> 不进入事项、提醒或通知闭环,不清空当前有效 BotContext
```
`context_summary` 也必须保存为一条 `SecretaryMessage(intent_type=context_summary)`,便于调试页回看“老板什么时候要求回顾、系统回了什么”。
@@ -500,17 +516,17 @@ qa / realtime_qa / note / context_summary
JSON 非法 / schema 校验失败
-> 不创建可确认草稿
-> 可选创建 FAILED 草稿仅用于调试
-> 可选创建 parse_failed 草稿仅用于调试;如果无法创建最小草稿,failure_records.target_type 使用 model_call_log
-> FailureRecord(ai_parse_failed)
```
`FAILED` 草稿不能确认、不能转换为任务/提醒、不能发送通知,只能用于调试追踪。
`parse_failed` 草稿不能确认、不能转换为任务/提醒、不能发送通知,只能用于调试追踪。
取消流转:
```text
PENDING_CONFIRM
-> CANCELLED
pending_confirmation
-> cancelled
-> 清空 BotContext
awaiting_more_info 且还没有 ai_drafts
@@ -518,7 +534,7 @@ awaiting_more_info 且还没有 ai_drafts
-> 记录 OperationLog(cancel_context)
```
`NEED_MANAGER_CONFIRM` 阶段如需取消或退回,归程经理确认模块或任务模块处理;01 只负责展示状态和记录操作,不再把它当作可补充草稿处理。
复杂事项壳 `pending_manager_confirm` 阶段如需取消或退回,归程经理确认模块或任务模块处理;01 只负责展示草稿来源和记录操作,不再把它当作可补充草稿处理。
## 9. 失败和日志
@@ -756,7 +772,7 @@ awaiting_more_info 且还没有 ai_drafts
- 返回 `reply_type=error`
- `answer=暂时没处理好,请稍后再试。`
- `failure.type=ai_parse_failed`
- 如落 `FAILED` 草稿,仅用于调试,不能确认或转换。
- 如落 `parse_failed` 草稿,仅用于调试,不能确认或转换。
### 10.15 低置信度追问
@@ -776,7 +792,7 @@ awaiting_more_info 且还没有 ai_drafts
### 10.16 已发布后修改
前置:上一条草稿已经 `CONVERTED``BotContext` 已清空。
前置:上一条草稿已经 `converted``BotContext` 已清空。
输入:
@@ -864,10 +880,10 @@ awaiting_more_info 且还没有 ai_drafts
期望:
- 老板确认后草稿进入 `NEED_MANAGER_CONFIRM`
- 老板确认后草稿进入 `confirmed`
- 清空老板侧 `BotContext`
- 不直接调用 `TaskAdapter` 转换任务
- 程经理确认后草稿进入 `CONFIRMED`,再转换为任务并进入 `CONVERTED`
- 创建 `tasks.status=pending_manager_confirm` 的事项壳,不直接通知最终接收人
- 程经理确认后,事项壳进入 `pending_notify` 并通知接收人;草稿在事项壳创建成功后进入 `converted`
## 11. Review 重点
@@ -880,7 +896,7 @@ awaiting_more_info 且还没有 ai_drafts
- 是否低置信度时追问老板,而不是默认修改当前草稿。
- 是否 `ai_drafts.status` 和非草稿处理状态已经拆清,`need_more_info/answered` 不误落草稿表。
- 是否所有 AI 输出都做 JSON 校验。
- 是否 `qa/realtime_qa/note/need_more_info/unsupported` 没有误生成草稿
- 是否 `qa/realtime_qa/note/need_more_info/unknown/unsupported` 没有误进入事项、提醒或通知闭环
- 是否普通任务不强制时间。
- 是否提醒缺时间会追问。
- 是否 AI 只生成草稿,不直接执行。
@@ -907,8 +923,8 @@ awaiting_more_info 且还没有 ai_drafts
- 是否 `secretary_messages` 只追加、`bot_contexts` 可覆盖、`bot_context_snapshot` 不作为最新状态。
- 是否 PostgreSQL 作为唯一事实源,AI 记忆表不替代正式草稿、任务、提醒、通知和反馈表状态。
- 是否 PostgreSQL AI 记忆读写异常时会降级并写 `FailureRecord(memory_store_failed)` 或应用日志/告警。
- 是否草稿 `CONFIRMED / CONVERTED / CANCELLED` 后立即清空上下文,后续修改不再直接改原草稿。
- 是否 `manager_confirm_required``NEED_MANAGER_CONFIRM -> 程经理确认 -> CONFIRMED -> CONVERTED`,不会绕过程经理确认。
- 是否草稿 `confirmed / converted / cancelled / superseded / expired` 后立即清空或失效上下文,后续修改不再直接改原草稿。
- 是否 `manager_confirm_required``confirmed -> 创建 pending_manager_confirm 事项壳 -> converted -> 程经理确认事项壳 -> pending_notify`,不会绕过程经理确认。
- 是否转换失败写 `FailureRecord(draft_convert_failed)`,且不回滚老板确认。
- 是否上下文回顾默认范围为最近 10 条消息、当前待确认草稿和最近 3 条已确认/已转换草稿。
- 是否 `/api/feishu/callback` 的验签、去重和鉴权没有写进本模块。
+7 -7
View File
@@ -6,8 +6,8 @@
## 2. 第一版做什么
1. 事项可以来自 AI 草稿 `CONFIRMED` 后转换。
2. 事项可以由程经理确认复杂草稿后生成,复杂草稿来自 `route_type=manager_confirm_required`
1. 事项可以来自 AI 草稿 `confirmed` 后转换。
2. 复杂事项来自 `route_type=manager_confirm_required`,老板确认后先创建 `pending_manager_confirm` 的事项壳
3. 事项可以由老板或程经理手动创建。
4. 普通员工第一版不默认给别人创建事项。
5. 事项必须有发起人、接收人、事项内容、反馈要求和状态。
@@ -36,7 +36,7 @@
```text
AI 草稿或手动输入
-> 人工确认
-> AI 草稿进入 CONFIRMED
-> AI 草稿进入 confirmed
-> 创建 tasks
-> 创建 notifications
-> 飞书通知接收人
@@ -49,11 +49,11 @@ AI 草稿或手动输入
```text
老板确认转程经理
-> AI 草稿进入 NEED_MANAGER_CONFIRM
-> 创建 pending_manager_confirm 事项或待确认记录
-> AI 草稿进入 confirmed
-> 创建 pending_manager_confirm 事项壳,接收人和内容可由程经理补齐
-> 给程经理发飞书提醒
-> 程经理进入平台确认、修改和分发
-> 草稿进入 CONFIRMED 后转换任务
-> 事项壳进入 pending_notify
-> 通知接收人
-> 接收反馈
```
@@ -101,7 +101,7 @@ AI 草稿或手动输入
1. 未确认事项不得通知接收人。
2. `pending_manager_confirm` 必须由程经理确认后才能进入通知流程。
3. 01 模块中的 `NEED_MANAGER_CONFIRM` 不能绕过程经理直接创建正式任务
3. `manager_confirm_required` 不能绕过程经理直接通知最终接收人;必须先创建 `pending_manager_confirm` 事项壳
4. 通知失败进入 `notify_failed`,不得假装已通知。
5. 已取消事项不能反馈、通知或补发。
6. 有问题反馈会将事项标记为 `problem`,并记录原因。
+28 -8
View File
@@ -16,6 +16,8 @@
8. 接收卡片按钮回调。
9. 支持接收人反馈已收到、处理中、已完成、有问题。
10. 记录飞书事件、通知发送结果和回调处理结果。
11. 旧卡片、失效卡片和重复点击必须幂等处理。
12. 平台内“有问题反馈”页需要在飞书手机端最小可用。
## 3. 第一版不做
@@ -59,7 +61,9 @@
-> 验签
-> 记录 feishu_events
-> 查找 notification / draft / task / reminder
-> 校验 event_id 或 idempotency_key
-> 校验操作人权限
-> 校验通知、草稿和卡片仍有效
-> 执行业务动作
-> 写 operation_logs
-> 返回飞书处理结果
@@ -80,7 +84,7 @@
9. `failure_records`
10. `operation_logs`
字段以 `docs/contracts/数据对象约定.md` 为准。
字段以 `docs/contracts/数据对象约定.md` 为准;权限、安全、日志脱敏、验签和幂等以 `docs/contracts/安全权限日志约定.md` 为准。
## 6. 接口需求
@@ -108,11 +112,13 @@
状态以 `docs/contracts/状态流转约定.md` 为准。幂等约束:
1. 同一个飞书 `event_id` 只处理一次。
1. 同一个飞书 `event_id` 只处理一次;没有稳定 `event_id` 时必须使用 `idempotency_key`
2. 同一个通知只允许一个有效发送结果。
3. 同一个卡片按钮重复点击不得重复创建事项、提醒或反馈。
4. 同一个机器人消息还必须按 `source + message_id` 在老板秘书入口二次幂等,不重复追加 `secretary_messages`
5. 回调处理失败必须标记 `feishu_events.process_status = failed` 并写失败记录
4. 草稿确认类卡片必须匹配 `ai_drafts.active_card_notification_id`
5. 旧卡片、失效卡片、已替代草稿和已过期通知的回调只写入 `feishu_events`,标记 `ignored`,不得写业务对象
6. 同一个机器人消息还必须按 `source + message_id` 在老板秘书入口二次幂等,不重复追加 `secretary_messages`
7. 回调处理失败必须标记 `feishu_events.process_status = failed` 并写失败记录。
## 9. 失败和日志
@@ -128,9 +134,19 @@
8. 操作人无权操作。
9. 有问题反馈缺少原因。
普通日志不得打印飞书 token、App Secret、完整手机号或完整邮箱。
普通日志不得打印飞书 token、App Secret、OAuth code/state、一次性操作 token、回调验签密钥、完整手机号或完整邮箱。
## 10. 给 AI 的测试样例
## 10. 平台内反馈页
飞书卡片无法直接完成“有问题”原因填写时,允许跳转平台内反馈页。第一版不做完整手机网页适配,但该页在飞书手机端必须最小可用:
1. 能通过飞书登录态或一次性反馈 token 识别反馈人。
2. 一次性反馈 token 只保存 hash,不保存明文。
3. 只展示事项或提醒摘要、反馈状态选择和问题原因输入。
4. 操作人不是接收人、链接过期、通知失效或卡片失效时,只提示不可反馈并记录事件或失败,不写业务对象。
5. 提交成功后写入 `feedbacks` 并按状态流转更新事项或提醒。
## 11. 给 AI 的测试样例
1. 验签失败的回调应返回拒绝并写失败记录。
2. 同一 `event_id` 重放应幂等,不重复创建反馈。
@@ -138,11 +154,15 @@
4. 老板点击取消草稿后,草稿不能再转换。
5. 非老板私聊机器人不应调用 AI。
6. 飞书通知失败时,应写 `failure_records` 并保留通知失败状态。
7. 没有稳定 `event_id` 的卡片回调应使用 `idempotency_key` 幂等。
8. 旧确认卡片或已失效通知的回调只记录 `feishu_events`,不得创建事项、提醒或反馈。
9. 飞书手机端平台反馈页必须能提交“有问题”原因,并拒绝过期 token。
## 11. Review 重点
## 12. Review 重点
1. 回调是否验签。
2. 回调是否幂等。
2. 回调是否使用 `event_id``idempotency_key` 幂等。
3. 飞书身份是否没有直接绕过平台权限。
4. 通知内容是否只发摘要和链接。
5. 失败是否可复盘。
6. 旧卡片、失效卡片是否不会继续写业务对象。
+4 -2
View File
@@ -9,7 +9,7 @@
1. 支持一次性提醒。
2. 支持每天、每周、每月固定周期提醒。
3. 支持提醒来自手动创建。
4. 支持提醒来自老板 AI 草稿 `CONFIRMED` 后转换。
4. 支持提醒来自老板 AI 草稿 `confirmed` 后转换。
5. 支持提醒与事项关联。
6. 到点后通知接收人。
7. 通知成功或失败都要记录。
@@ -44,6 +44,7 @@
```text
scheduler 扫描 due reminders
-> 校验提醒状态
-> 用事务锁定当前批次
-> 生成幂等键
-> 创建 notifications
-> 调用飞书发送
@@ -107,7 +108,7 @@ scheduler 扫描 due reminders
2. `paused` 不触发,但可以恢复。
3. `cancelled` 不触发、不可恢复。
4. `trigger_failed` 必须写失败记录。
5. 同一个提醒同一触发时间同一接收人只能生成一条有效通知。
5. 同一个提醒同一触发时间同一接收人同一渠道只能生成一条有效通知。
6. 一次性提醒成功触发后进入 `triggered`;周期提醒成功触发后保持 `active` 并计算下一次 `next_trigger_at`
7. 提醒反馈不改变提醒调度状态,反馈结果通过 `feedbacks` 展示。
8. AI 提醒草稿缺少明确时间时不得创建 active 提醒,应由 01 模块追问或标记 `missing_fields`
@@ -139,3 +140,4 @@ scheduler 扫描 due reminders
3. 是否不支持 cron 等第一版不做内容。
4. scheduler 是否走 service 层而不是直接改业务表。
5. 失败是否可复盘。
6. 是否使用事务锁和 `notifications.idempotency_key` 唯一约束防止重复触发。
@@ -4,6 +4,8 @@
权限、日志和失败记录模块负责保证第一版闭环可控、可追溯、可复盘。权限必须在后端校验,关键操作必须写日志,AI、飞书、调度和业务失败必须形成失败记录。
权限、安全、日志脱敏、飞书验签幂等和访问边界的全局事实源是 `docs/contracts/安全权限日志约定.md`;本 spec 只说明模块目标和业务补充。
## 2. 第一版做什么
1. 定义老板、程经理、普通员工、管理员 / AI 团队四类角色。
@@ -68,6 +70,8 @@
字段以 `docs/contracts/数据对象约定.md` 为准。
安全和访问边界以 `docs/contracts/安全权限日志约定.md` 为准。
## 6. 接口需求
主要接口:
@@ -92,6 +96,7 @@
5. 普通员工不能给别人创建事项或提醒。
6. 反馈人必须是事项或提醒接收人。
7. 飞书登录用户必须匹配到启用状态的平台用户后才能进入平台。
8. 模型调用日志、飞书原始 payload、失败记录原始错误和一次性操作 token 明文不得对普通员工开放。
## 8. 状态流转
@@ -129,6 +134,8 @@
5. 日志中不得包含完整手机号、完整邮箱、飞书 token 或密钥。
6. 未匹配平台用户的飞书登录应失败并写失败记录。
7. PostgreSQL AI 记忆读写失败时,应写 `memory_store_failed`,不得继续依赖内存确认草稿。
8. 飞书旧卡片或失效卡片回调只记录事件,不写业务对象。
9. 一次性操作 token 只能保存 hash,普通日志不得出现明文。
## 11. Review 重点
@@ -137,3 +144,4 @@
3. 失败记录是否包含关联对象、失败类型和处理结果。
4. 日志是否记录操作人和渠道。
5. 敏感信息是否被脱敏。
6. 访问范围是否符合 `安全权限日志约定.md`