Version: 0.9.77.dev.260505
后端:
1.阶段 6 CP4/CP5 目录收口与共享边界纯化
- 将 backend 根目录收口为 services、client、gateway、cmd、shared 五个一级目录
- 收拢 bootstrap、inits、infra/kafka、infra/outbox、conv、respond、pkg、middleware,移除根目录旧实现与空目录
- 将 utils 下沉到 services/userauth/internal/auth,将 logic 下沉到 services/schedule/core/planning
- 将迁移期 runtime 桥接实现统一收拢到 services/runtime/{conv,dao,eventsvc,model},删除 shared/legacy 与未再被 import 的旧 service 实现
- 将 gateway/shared/respond 收口为 HTTP/Gin 错误写回适配,shared/respond 仅保留共享错误语义与状态映射
- 将 HTTP IdempotencyMiddleware 与 RateLimitMiddleware 收口到 gateway/middleware
- 将 GormCachePlugin 下沉到 shared/infra/gormcache,将共享 RateLimiter 下沉到 shared/infra/ratelimit,将 agent token budget 下沉到 services/agent/shared
- 删除 InitEino 兼容壳,收缩 cmd/internal/coreinit 仅保留旧组合壳残留域初始化语义
- 更新微服务迁移计划与桌面 checklist,补齐 CP4/CP5 当前切流点、目录终态与验证结果
- 完成 go test ./...、git diff --check 与最终真实 smoke;health、register/login、task/create+get、schedule/today、task-class/list、memory/items、agent chat/meta/timeline/context-stats 全部 200,SSE 合并结果为 CP5_OK 且 [DONE] 只有 1 个
This commit is contained in:
136
backend/shared/infra/outbox/route_registry.go
Normal file
136
backend/shared/infra/outbox/route_registry.go
Normal file
@@ -0,0 +1,136 @@
|
||||
package outbox
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var outboxRouteRegistry = struct {
|
||||
sync.RWMutex
|
||||
eventToService map[string]string
|
||||
serviceRoutes map[string]ServiceRoute
|
||||
}{
|
||||
eventToService: make(map[string]string),
|
||||
serviceRoutes: make(map[string]ServiceRoute),
|
||||
}
|
||||
|
||||
// RegisterServiceRoute 注册或覆盖某个服务的物理 outbox 路由。
|
||||
//
|
||||
// 职责边界:
|
||||
// 1. 只登记“服务 -> table/topic/group”目录,不登记事件归属;
|
||||
// 2. 同服务重复注册时以后者覆盖前者,方便显式配置覆盖默认目录;
|
||||
// 3. 空服务名直接报错,避免把共享 topic 误当成新终态。
|
||||
func RegisterServiceRoute(route ServiceRoute) error {
|
||||
route = normalizeServiceRoute(route)
|
||||
if route.ServiceName == "" {
|
||||
return errors.New("serviceName is empty")
|
||||
}
|
||||
|
||||
outboxRouteRegistry.Lock()
|
||||
defer outboxRouteRegistry.Unlock()
|
||||
|
||||
outboxRouteRegistry.serviceRoutes[route.ServiceName] = route
|
||||
return nil
|
||||
}
|
||||
|
||||
// RegisterEventService 记录“事件类型 -> 服务归属”的全局路由。
|
||||
//
|
||||
// 职责边界:
|
||||
// 1. 只登记跨进程都要识别的事件归属,不承载 handler 逻辑;
|
||||
// 2. 同一 event_type 只能归属一个服务,重复登记同值视为幂等;
|
||||
// 3. 若该服务还没有显式路由,则先写入默认服务目录,保证后续能查到 table/topic/group。
|
||||
func RegisterEventService(eventType, serviceName string) error {
|
||||
eventType = strings.TrimSpace(eventType)
|
||||
if eventType == "" {
|
||||
return errors.New("eventType is empty")
|
||||
}
|
||||
serviceName = normalizeServiceName(serviceName)
|
||||
if serviceName == "" {
|
||||
return errors.New("serviceName is empty")
|
||||
}
|
||||
|
||||
outboxRouteRegistry.Lock()
|
||||
defer outboxRouteRegistry.Unlock()
|
||||
|
||||
if existing, ok := outboxRouteRegistry.eventToService[eventType]; ok {
|
||||
if existing != serviceName {
|
||||
return fmt.Errorf("eventType %s already registered to service %s", eventType, existing)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
outboxRouteRegistry.eventToService[eventType] = serviceName
|
||||
return nil
|
||||
}
|
||||
|
||||
// ResolveEventService 查询某个事件类型的归属服务。
|
||||
//
|
||||
// 返回值说明:
|
||||
// 1. serviceName 为登记结果;
|
||||
// 2. ok=false 表示当前路由表里还没有这个事件类型的归属信息。
|
||||
func ResolveEventService(eventType string) (serviceName string, ok bool) {
|
||||
eventType = strings.TrimSpace(eventType)
|
||||
if eventType == "" {
|
||||
return "", false
|
||||
}
|
||||
|
||||
outboxRouteRegistry.RLock()
|
||||
defer outboxRouteRegistry.RUnlock()
|
||||
|
||||
serviceName, ok = outboxRouteRegistry.eventToService[eventType]
|
||||
return serviceName, ok
|
||||
}
|
||||
|
||||
// ResolveServiceRoute 查询某个服务的物理 outbox 配置。
|
||||
//
|
||||
// 返回值说明:
|
||||
// 1. route 始终返回一个可执行的目录结果,未显式注册时回退默认目录;
|
||||
// 2. ok=true 表示命中显式注册目录,ok=false 表示走默认目录;
|
||||
// 3. 这样既能支持显式配置覆盖,也能让基础设施在启动初期就有稳定默认值。
|
||||
func ResolveServiceRoute(serviceName string) (route ServiceRoute, ok bool) {
|
||||
serviceName = normalizeServiceName(serviceName)
|
||||
if serviceName == "" {
|
||||
return DefaultServiceRoute(""), false
|
||||
}
|
||||
|
||||
outboxRouteRegistry.RLock()
|
||||
route, ok = outboxRouteRegistry.serviceRoutes[serviceName]
|
||||
outboxRouteRegistry.RUnlock()
|
||||
if ok {
|
||||
return normalizeServiceRoute(route), true
|
||||
}
|
||||
if route, ok = configuredServiceRoute(serviceName); ok {
|
||||
return route, true
|
||||
}
|
||||
return DefaultServiceRoute(serviceName), false
|
||||
}
|
||||
|
||||
// ResolveEventRoute 先按事件查服务,再按服务查物理目录。
|
||||
//
|
||||
// 返回值说明:
|
||||
// 1. route 包含事件所在服务的 table/topic/group;
|
||||
// 2. ok=true 只表示“事件 -> 服务归属”已登记;
|
||||
// 3. 服务目录若未显式注册,会自动回退到默认目录。
|
||||
func ResolveEventRoute(eventType string) (route ServiceRoute, ok bool) {
|
||||
serviceName, ok := ResolveEventService(eventType)
|
||||
if !ok {
|
||||
return ServiceRoute{}, false
|
||||
}
|
||||
route, _ = ResolveServiceRoute(serviceName)
|
||||
return route, true
|
||||
}
|
||||
|
||||
func configuredServiceRoute(serviceName string) (ServiceRoute, bool) {
|
||||
cfg, ok := ResolveServiceConfig(serviceName)
|
||||
if !ok {
|
||||
return ServiceRoute{}, false
|
||||
}
|
||||
return normalizeServiceRoute(ServiceRoute{
|
||||
ServiceName: cfg.Name,
|
||||
TableName: cfg.TableName,
|
||||
Topic: cfg.Topic,
|
||||
GroupID: cfg.GroupID,
|
||||
}), true
|
||||
}
|
||||
Reference in New Issue
Block a user