Version: 0.9.68.dev.260504
后端: 1. 阶段 3 notification 服务边界落地,新增 `cmd/notification`、`services/notification`、`gateway/notification`、`shared/contracts/notification` 和 notification port,按 userauth 同款最小手搓 zrpc 样板收口 2. notification outbox consumer、relay 和 retry loop 迁入独立服务入口,处理 `notification.feishu.requested`,gateway 改为通过 zrpc client 调用 notification 3. 清退旧单体 notification DAO/model/service/provider/runner 和 `service/events/notification_feishu.go`,旧实现不再作为活跃编译路径 4. 修复 outbox 路由归属、dispatch 启动扫描、Kafka topic 探测/投递超时、sending 租约恢复、毒消息 MarkDead 错误回传和 RPC timeout 边界 5. 同步调整 active-scheduler 触发通知事件、核心 outbox handler、MySQL 迁移边界和 notification 配置 文档: 1. 更新微服务迁移计划,将阶段 3 notification 标记为已完成,并明确下一阶段从 active-scheduler 开始
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
2. 阶段 1 已完成:当前基线已经切成服务级 outbox 表、服务级 Kafka topic、服务级 consumer group;仍在单体进程内装配多个服务级 worker,后续拆微服务时再物理迁出。
|
||||
3. 阶段 1.5 / 1.6 已完成:`backend/services/llm` 和 `backend/services/rag` 已经是当前 canonical 入口,`backend/infra/llm` 和 `backend/infra/rag` 的 `.go` 旧实现已删除。
|
||||
4. 阶段 2 已完成:`user/auth` 已经从 Gin 单体抽成 `cmd/userauth` + `services/userauth` 的 go-zero zrpc 服务边界,gateway 只保留 user HTTP 入口、鉴权、额度门禁和轻量转发。
|
||||
5. 下一轮从阶段 3 开始,默认目标是拆 `notification`;不要再把 outbox、llm-service、rag-service 或 user/auth 当成未完成待办。
|
||||
5. 阶段 3 `notification` 服务化已完成实现、code review 修复和真实 smoke;不要再把 outbox、llm-service、rag-service 或 user/auth 当成未完成待办。
|
||||
|
||||
本计划遵守两个硬原则:
|
||||
|
||||
@@ -97,6 +97,8 @@ gozero 服务负责领域能力:
|
||||
> 当前状态:`llm-service` / `rag-service` 这两个边界已经先做成 `backend/services/*` 的服务内模块,调用仍由 `backend/cmd/start.go` 在同一进程内装配,不是 gozero 独立进程。
|
||||
>
|
||||
> 当前状态:`user/auth` 已经完成 go-zero zrpc 独立进程拆分,是阶段 2 样板。服务端在 `backend/services/userauth`,进程入口在 `backend/cmd/userauth`,gateway client 在 `backend/gateway/userauth`。
|
||||
>
|
||||
> 当前状态:`notification` 已经完成阶段 3 拆分。服务端在 `backend/services/notification`,进程入口在 `backend/cmd/notification`,gateway client 在 `backend/gateway/notification`,服务级 outbox consumer 和 retry loop 已随服务入口迁出。
|
||||
|
||||
### 3.3 事件层
|
||||
|
||||
@@ -127,6 +129,7 @@ gozero 服务负责领域能力:
|
||||
5. `infra` 也不应该是一个大公共篮子:像 `kafka`、`outbox` 这类跨服务底座可以放到 `shared/infra`;`llm-service`、`rag-service` 这类模型与检索能力要单独成基础设施服务,不要塞进 `shared`;`prompt`、`tooling` 这类强业务依赖的适配器则应跟着具体服务走。
|
||||
6. 换句话说,`shared` 是“跨进程契约层 + 少量跨服务底座”,不是“公共业务层”。
|
||||
7. 阶段 2 已经新增 `backend/shared/contracts/userauth` 和 `backend/shared/ports`,只承载跨层契约和端口接口;user/auth 的 JWT、DAO、额度治理、黑名单实现不进入 `shared`。
|
||||
8. 阶段 3 已经新增 `backend/shared/contracts/notification`,只承载 notification 跨层 DTO;通知通道 DAO、投递状态机、provider、重试策略和 outbox handler 都留在 `backend/services/notification`。
|
||||
|
||||
---
|
||||
|
||||
@@ -141,7 +144,7 @@ gozero 服务负责领域能力:
|
||||
| 1.5 | 先抽 llm-service(已完成) | 已完成,`backend/services/llm` 作为当前 canonical 入口 | `go test ./...` + course / active-scheduler / memory 模型调用 smoke |
|
||||
| 1.6 | 再抽 rag-service(已完成) | 已完成,`backend/services/rag` 作为当前 canonical 入口 | `go test ./...` + memory retrieve / rerank smoke |
|
||||
| 2 | 先拆 user/auth(已完成) | 已完成,阶段 2 样板 commit 点:userauth zrpc、gateway userapi、JWT/黑名单/额度治理、启动与迁移边界已收口 | 已完成注册/登录/刷新/并发 refresh/登出/鉴权/token quota smoke |
|
||||
| 3 | 再拆 notification(下一阶段) | notification 服务能独立消费和重试后 commit | notification E2E smoke + worker-only smoke |
|
||||
| 3 | 再拆 notification(已完成) | 已完成,`cmd/notification` + `services/notification` zrpc / outbox consumer / retry loop 已收口,旧单体实现已删除;是否 commit 等用户明确要求 | 已完成 notification E2E smoke + worker-only smoke |
|
||||
| 4 | 再拆 active-scheduler | 预览生成和确认链路通过 gozero 服务跑通后 commit | dry-run / preview / confirm smoke |
|
||||
| 5 | 再拆 schedule / task / course / task-class | 每个领域完成一次切流就 commit 一次 | schedule/task/course/task-class 回归 + 全链路 smoke |
|
||||
| 6 | 再拆 agent / memory | agent 编排服务、memory 支撑服务和后台 worker 独立后 commit | agent chat / SSE / memory extract / memory retrieve smoke |
|
||||
@@ -373,6 +376,14 @@ flowchart LR
|
||||
3. 重试扫描 smoke。
|
||||
4. 停掉 notification 服务后,主动调度预览仍然可用的回归测试。
|
||||
|
||||
本轮收口状态(2026-05-04):
|
||||
|
||||
1. `cmd/notification` 已承载 notification zrpc 启动、DB 迁移、服务级 outbox consumer 和重试扫描。
|
||||
2. `backend/services/notification` 已收进 DAO、model、sv、rpc、飞书 provider 和 outbox handler;gateway 通过 `backend/gateway/notification` zrpc client 调用。
|
||||
3. 主动调度侧只写入 `notification.feishu.requested`,publisher 侧只注册事件归属到 `notification`,不再启动单体 notification consumer。
|
||||
4. 旧 `backend/notification`、旧 DAO/model 和旧 `service/events/notification_feishu.go` 已删除;review 发现的 sending 租约恢复和 RPC timeout 边界已修复。
|
||||
5. 真实 smoke 已通过:`notification_outbox_messages.id=3` 已从 `pending` 推进到 `consumed`,`smartflow.notification.outbox` 已出现 `outbox_id=3`,对应 `notification_records` 生成并按未启用通道进入 `skipped`。
|
||||
|
||||
---
|
||||
|
||||
### 4.8 阶段 4:再拆 active-scheduler
|
||||
@@ -498,20 +509,19 @@ flowchart LR
|
||||
|
||||
当前建议按这个顺序推进:
|
||||
|
||||
注:阶段 1.5 / 1.6 / 2 已完成,当前实际推进从阶段 3 `notification` 开始。
|
||||
注:阶段 1.5 / 1.6 / 2 / 3 已完成;`notification` 已完成实现、code review 修复和真实 smoke,不再作为下一轮待办。
|
||||
|
||||
1. 以阶段 1 的服务级 outbox 为当前基线,不再回头做共享 outbox 方案。
|
||||
2. 保持 `backend/services/llm` 和 `backend/services/rag` 为 canonical 入口,不再把它们写成待办。
|
||||
3. 保持 `backend/services/userauth` + `cmd/userauth` 为阶段 2 样板,不再回头恢复 Gin 单体 user/auth。
|
||||
4. 下一步切 notification。
|
||||
5. 再切 active-scheduler。
|
||||
6. 然后切 schedule / task / course / task-class。
|
||||
7. 再切 agent / memory,把聊天编排和记忆链路独立出去。
|
||||
8. 最后把 Gin 收口成纯 Gateway。
|
||||
4. 下一步进入阶段 4,优先切 `active-scheduler`。
|
||||
5. 然后切 schedule / task / course / task-class。
|
||||
6. 再切 agent / memory,把聊天编排和记忆链路独立出去。
|
||||
7. 最后把 Gin 收口成纯 Gateway。
|
||||
|
||||
一句话总结:
|
||||
|
||||
> outbox 的服务级基础设施、llm-service、rag-service 和 user/auth 样板服务都已经完成;下一轮从 notification 开始,把通知投递和重试切成独立服务;然后让 active-scheduler、schedule、task、course、task-class 按稳定边界逐步独立;再把 agent / memory 独立出来,完成聊天编排和记忆链路的服务化;最后把 Gin 收口成真正的 Gateway。
|
||||
> outbox 的服务级基础设施、llm-service、rag-service、user/auth 样板服务和 notification 阶段 3 都已经完成;下一步让 active-scheduler、schedule、task、course、task-class 按稳定边界逐步独立;再把 agent / memory 独立出来,完成聊天编排和记忆链路的服务化;最后把 Gin 收口成真正的 Gateway。
|
||||
|
||||
---
|
||||
|
||||
@@ -582,17 +592,14 @@ SmartFlow-Agent/
|
||||
│ │ │ ├── batch/
|
||||
│ │ │ └── item/
|
||||
│ │ ├── notification/
|
||||
│ │ │ ├── start.go
|
||||
│ │ │ ├── handler.go
|
||||
│ │ │ ├── sv/
|
||||
│ │ │ ├── dao/
|
||||
│ │ │ ├── model/
|
||||
│ │ │ └── internal/
|
||||
│ │ │ ├── provider/
|
||||
│ │ │ ├── runner/
|
||||
│ │ │ ├── dedupe/
|
||||
│ │ │ ├── channel/
|
||||
│ │ │ └── retry/
|
||||
│ │ │ ├── internal/
|
||||
│ │ │ │ └── feishu/
|
||||
│ │ │ └── rpc/
|
||||
│ │ │ ├── pb/
|
||||
│ │ │ └── notification.proto
|
||||
│ │ ├── active-scheduler/
|
||||
│ │ │ ├── start.go
|
||||
│ │ │ ├── handler.go
|
||||
@@ -698,19 +705,19 @@ SmartFlow-Agent/
|
||||
> 2. `backend/gateway/userapi/*` 是 user HTTP 入口,`backend/gateway/userauth/*` 是 userauth zrpc client,二者都属于 gateway 边缘层。
|
||||
> 3. `backend/service/*.go` 这批现有业务逻辑,后面要分别迁到各自服务根目录下的 `sv/`。
|
||||
> 4. `backend/service/agentsvc/*` 和 `backend/newAgent/*`,后面要收束到 `backend/services/agent/sv/` + `internal/{prompt,graph,stream,tool,session,router}`。
|
||||
> 5. `backend/notification/*`,下一阶段要收束到 `backend/services/notification/`,其中 `runner/provider/dedupe/channel_service` 归入 `sv/` 或 `internal/notification/`。
|
||||
> 5. `backend/services/notification/*` 已经是阶段 3 终态样板;`backend/cmd/notification` 是独立进程入口,`backend/gateway/notification` 是 gateway 侧 zrpc client,`backend/shared/contracts/notification` 只放跨层契约;旧 `backend/notification/*`、旧 DAO/model 和旧 `service/events/notification_feishu.go` 不再作为活跃实现。
|
||||
> 6. `backend/active_scheduler/*`,后面要收束到 `backend/services/active-scheduler/`,其中 `graph/selection/feedbacklocate/apply/job` 归入 `internal/`。
|
||||
> 7. `backend/memory/*`,后面要收束到 `backend/services/memory/`;当前 `memory/service/*` 只是迁移过渡态,终态还是按 `sv/` 或 `internal/` 拆开。
|
||||
>
|
||||
> 说明 4:`shared` 先保留 `events` 和少量跨服务底座型 `infra`。以后如果真的出现跨服务 DTO / 枚举 / 常量,再新增 `contracts` 一类目录,但不要把 `dao`、`model`、`sv`、`handler` 这类服务私有层塞进去。
|
||||
|
||||
> 说明 5:`notification` 和 `active-scheduler` 的服务内部建议继续收束成你熟悉的“服务内单体壳”风格,不要让一级目录一直长成一排小框架;复杂算法和编排细节可以继续拆文件,但尽量下沉到 `sv/` 或 `internal/` 下面。
|
||||
> 说明 5:`notification` 已经按 `userauth` 同款最小手搓 zrpc 样板收口:`rpc/server.go`、`rpc/handler.go`、`rpc/errors.go` + `rpc/pb`,不是 goctl 自动脚手架;`active-scheduler` 后续也按服务内单体壳继续收束,不要让一级目录长期长成一排小框架。
|
||||
>
|
||||
> 说明 6:`llm-service` 和 `rag-service` 是独立基础设施服务,不放进 `shared`;`rag-service` 依赖 `llm-service` 做 embedding / rerank,不反向依赖业务服务。
|
||||
>
|
||||
> 说明 7:目录树里如果暂时写成 `backend/services/llm/` 和 `backend/services/rag/`,那只是目录名写法;后文所有职责判断都以 `llm-service` / `rag-service` 这两个逻辑服务名为准。
|
||||
>
|
||||
> 说明 8:阶段 2 已经采用 `backend/services/userauth/` 作为实际目录名,不再使用 `user-auth`。gateway 侧 zrpc client 放在 `backend/gateway/userauth/`,进程入口放在 `backend/cmd/userauth/`;不要把 rpc client 放进 `cmd`。
|
||||
> 说明 8:阶段 2 已经采用 `backend/services/userauth/` 作为实际目录名,不再使用 `user-auth`。阶段 3 已经采用 `backend/services/notification/` 作为实际目录名。gateway 侧 zrpc client 放在 `backend/gateway/{userauth,notification}/`,进程入口放在 `backend/cmd/{userauth,notification}/`;不要把 rpc client 放进 `cmd`。
|
||||
|
||||
### 6.3 哪些可以不用变
|
||||
|
||||
@@ -723,7 +730,7 @@ SmartFlow-Agent/
|
||||
|
||||
1. `backend/cmd/start.go` 这种“大装配入口”后面要逐步拆成 gateway 启动和各服务启动。
|
||||
2. `api` 这一层会收缩成纯 Gateway 职责,不再承载核心领域逻辑。
|
||||
3. 当前仓库里的 `backend/service` 目录和相关遗留入口,要按 `user/auth`、`course`、`task-class`、`notification`、`active-scheduler`、`schedule`、`task`、`agent`、`memory` 拆出去;其中 `notification` 和 `active-scheduler` 最终都要收束成更像 seckill 的服务内单体壳,不要长期维持一串顶层小包。
|
||||
3. 当前仓库里的 `backend/service` 目录和相关遗留入口,要继续按 `course`、`task-class`、`active-scheduler`、`schedule`、`task`、`agent`、`memory` 拆出去;`user/auth` 和 `notification` 已完成独立服务边界,后续不要回迁到单体。
|
||||
4. 当前单体里的共享启动方式 `api / worker / all`,后面会拆成“gateway 进程 + 服务进程 + worker 进程”的组合。
|
||||
5. 任何依赖 `users` 表直读、核心表直写的网关路径,都要迁到对应服务里。
|
||||
6. 不再把服务私有的 `dao` / `model` / `sv` / `handler` 误放进 `shared`,避免它变成新的单体公共层。
|
||||
@@ -847,7 +854,8 @@ graph TD
|
||||
4. 图里的 outbox 是“每个服务自己的 outbox 表 + 专属 relay worker”的抽象,不代表所有服务共用一张表。
|
||||
5. 当前阶段 1 已完成 `agent`、`task`、`memory`、`active-scheduler`、`notification` 的服务级 outbox 表、topic 和 consumer group;尚未物理拆出的服务后续沿用同一模式补齐。
|
||||
6. 当前阶段 2 已完成 `user/auth` 物理拆分;gateway 到 userauth 的调用已经通过 zrpc client,不再通过本地 DAO/service。
|
||||
7. Kafka 是共享运输层,不是共享业务 topic;新流量不应再默认进入单一共享 topic。
|
||||
7. 当前阶段 3 已完成 `notification` 物理拆分;gateway 到 notification 的调用已经通过 zrpc client,notification outbox consumer、relay 和 retry loop 已迁入 `cmd/notification` 启动边界。
|
||||
8. Kafka 是共享运输层,不是共享业务 topic;新流量不应再默认进入单一共享 topic。
|
||||
|
||||
### 6.9 切对话交接卡
|
||||
|
||||
@@ -857,11 +865,11 @@ graph TD
|
||||
2. 已冻结的终态是 `Gin Gateway + gozero 服务群 + 服务级 outbox + Kafka 共享运输层`。
|
||||
3. 阶段 1 已完成,当前 outbox 基线是服务级表、服务级 topic、服务级 consumer group;worker 仍在单体内装配,后续随对应服务迁出。
|
||||
4. 阶段 2 已完成,`user/auth` 已经是样板服务,不要再把它当成下一轮待办。
|
||||
5. 下一轮默认从阶段 3 `notification` 开始;`llm-service`、`rag-service` 也已完成,不要重新当成待办。
|
||||
6. `notification` 和 `active-scheduler` 后续要回到更像 seckill 的服务内单体壳。
|
||||
5. 阶段 3 `notification` 已完成实现、code review 修复和真实 smoke;`llm-service`、`rag-service` 也已完成,不要重新当成待办。
|
||||
6. 下一轮默认从阶段 4 `active-scheduler` 开始;它后续要回到更像 seckill 的服务内单体壳。
|
||||
7. `shared` 只保留跨进程契约和少量跨服务底座,不承载业务逻辑、DAO、模型或状态机。
|
||||
8. 如果后续要改目录,必须先回答“这个文件属于哪一个典型用例”,回答不清楚就先别动结构。
|
||||
9. 当前文档已经可以作为切对话基线;后续代理默认按本文件推进。现阶段的迁移基线入口是 `backend/cmd/api`、`backend/cmd/worker`、`backend/cmd/all`,它们只是当前仓库的启动壳,不是终态。`backend/cmd/userauth` 是阶段 2 的独立服务入口。终态仍然是“一个服务一个独立 `main.go`”,只在出现新的契约风险、边界变化或业务语义变化时再重新讨论架构。
|
||||
9. 当前文档已经可以作为切对话基线;后续代理默认按本文件推进。现阶段的迁移基线入口是 `backend/cmd/api`、`backend/cmd/worker`、`backend/cmd/all`,它们只是当前仓库的启动壳,不是终态。`backend/cmd/userauth` 是阶段 2 的独立服务入口,`backend/cmd/notification` 是阶段 3 的独立服务入口。终态仍然是“一个服务一个独立 `main.go`”,只在出现新的契约风险、边界变化或业务语义变化时再重新讨论架构。
|
||||
|
||||
### 6.10 启动方式与进程模型
|
||||
|
||||
@@ -984,7 +992,7 @@ graph TD
|
||||
|
||||
这段用于避免后续代理重复踩阶段 2 已经纠偏过的问题。
|
||||
|
||||
1. 阶段 3 起步默认是 `notification`,不是 outbox、llm-service、rag-service 或 user/auth。
|
||||
1. 阶段 3 `notification` 已完成;后续起步默认是阶段 4 `active-scheduler`,不是 outbox、llm-service、rag-service、user/auth 或 notification。
|
||||
2. 主代理负责 leader:先读必要文档和代码,拆任务,关键阻塞任务自己做;子代理只能承担并行、明确、非阻塞的侧翼任务。
|
||||
3. 如果确实有会影响切分方向的不确定点,先总结成拍板点问用户;文档已经写清楚的内容不要重复问。
|
||||
4. 查库一律用 `docker exec`。MySQL / Redis 都按这个规则走;不直接用本机客户端绕过容器。
|
||||
@@ -999,7 +1007,7 @@ graph TD
|
||||
|
||||
---
|
||||
|
||||
### 6.14 阶段 0 历史基线与阶段 1/2 当前基线快照
|
||||
### 6.14 阶段 0 历史基线与阶段 1/2/3 当前基线快照
|
||||
|
||||
阶段 0 历史基线:
|
||||
|
||||
@@ -1033,6 +1041,17 @@ graph TD
|
||||
5. `cmd/all` 不再迁 `users`,`cmd/userauth` 自己迁 `users` 和 `user_token_usage_adjustments`。
|
||||
6. 完整本地 smoke 需要同时启动 `cmd/all` 和 `cmd/userauth`。
|
||||
|
||||
阶段 3 当前基线:
|
||||
|
||||
1. `backend/cmd/notification/main.go` 是 notification 独立进程入口,负责 DB 迁移、zrpc server、notification outbox consumer 和 retry loop 的统一生命周期。
|
||||
2. `backend/services/notification` 拥有 notification 核心业务、DAO、模型、飞书 provider、幂等、投递记录状态机、重试扫描和 outbox handler。
|
||||
3. `backend/gateway/notification` 是 gateway 侧 zrpc client;gateway 只保留 notification HTTP 入口、鉴权和轻量组合逻辑,不再直连 notification DAO/service。
|
||||
4. `backend/shared/contracts/notification` 和 `backend/shared/ports` 只承载跨层契约和端口接口,不承载服务私有业务实现。
|
||||
5. notification 内部是 `userauth` 同款最小手搓 zrpc 框架,不使用 goctl 自动脚手架;`rpc` 只保留 `NewServer` 供 `cmd/notification` 管理 signal、outbox consumer、retry loop 和 server 生命周期。
|
||||
6. 旧 `backend/notification/*`、旧 `backend/dao/notification_channel.go`、旧 `backend/model/notification_channel.go` 和旧 `backend/service/events/notification_feishu.go` 已删除;若 `backend/notification` 目录壳仍存在,它不参与编译,也不作为活跃实现。
|
||||
7. notification outbox consumer 已迁入独立服务边界并处理 `notification.feishu.requested`,覆盖 payload/version 校验、dead/retry/consumed 状态推进和毒消息回退。
|
||||
8. 已完成真实 smoke:`notification_outbox_messages` 可从 `pending` 推进到 `consumed`,Kafka `smartflow.notification.outbox` 可看到对应 outbox 消息,`notification_records` 可生成幂等记录并按通道状态进入预期状态。
|
||||
|
||||
---
|
||||
|
||||
## 7. 风险与回退
|
||||
|
||||
Reference in New Issue
Block a user