Files
smartmate/backend/services/active_scheduler/dao/connect.go
Losita 4d9a5c4d30 Version: 0.9.69.dev.260504
后端:
1. 阶段 4 active-scheduler 服务边界落地,新增 `cmd/active-scheduler`、`services/active_scheduler`、`shared/contracts/activescheduler` 和 active-scheduler port,迁移 dry-run、trigger、preview、confirm zrpc 能力
2. active-scheduler outbox consumer、relay、retry loop 和 due job scanner 迁入独立服务入口,gateway `/active-schedule/*` 改为通过 zrpc client 调用
3. gateway 目录收口为 `gateway/api` + `gateway/client`,统一归档 userauth、notification、active-scheduler 的 HTTP 门面和 zrpc client
4. 将旧 `backend/active_scheduler` 领域核心下沉到 `services/active_scheduler/core`,清退旧根目录活跃实现,并补充 active-scheduler 启动期跨域依赖表检查
5. 调整单体启动与 outbox 归属,`cmd/all` 不再启动 active-scheduler workflow、scanner 或 handler

文档:
1. 更新微服务迁移计划,将阶段 4 active-scheduler 标记为首轮收口完成,并明确下一阶段进入 schedule / task / course / task-class
2026-05-04 21:01:00 +08:00

132 lines
5.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package dao
import (
"fmt"
outboxinfra "github.com/LoveLosita/smartflow/backend/infra/outbox"
coremodel "github.com/LoveLosita/smartflow/backend/model"
"github.com/spf13/viper"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
// OpenDBFromConfig 创建 active-scheduler 服务自己的数据库句柄。
//
// 职责边界:
// 1. 只迁移 active-scheduler 拥有的 trigger / preview / job / session 表和本服务 outbox 表;
// 2. 不迁移 task、schedule、agent、notification 或 user/auth 表,避免独立进程越权管理其它服务模型;
// 3. 返回的 *gorm.DB 供服务内主链路、due job scanner 和 outbox consumer 复用。
func OpenDBFromConfig() (*gorm.DB, error) {
host := viper.GetString("database.host")
port := viper.GetString("database.port")
user := viper.GetString("database.user")
password := viper.GetString("database.password")
dbname := viper.GetString("database.dbname")
dsn := fmt.Sprintf(
"%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
user, password, host, port, dbname,
)
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
return nil, err
}
if err = db.AutoMigrate(
&coremodel.ActiveScheduleJob{},
&coremodel.ActiveScheduleTrigger{},
&coremodel.ActiveSchedulePreview{},
&coremodel.ActiveScheduleSession{},
); err != nil {
return nil, fmt.Errorf("auto migrate active-scheduler tables failed: %w", err)
}
if err = autoMigrateActiveSchedulerOutboxTable(db); err != nil {
return nil, err
}
if err = ensureRuntimeDependencyTables(db); err != nil {
return nil, err
}
return db, nil
}
// autoMigrateActiveSchedulerOutboxTable 只迁移 active-scheduler 服务自己的 outbox 物理表。
//
// 职责边界:
// 1. 只负责 active-scheduler.outbox 对应表,不碰其它服务 outbox
// 2. 让独立 active-scheduler 服务可以单独发布 trigger 并消费 active_schedule.triggered
// 3. 若后续调整 outbox 表名,只改 service catalog不在这里硬编码。
func autoMigrateActiveSchedulerOutboxTable(db *gorm.DB) error {
cfg, ok := outboxinfra.ResolveServiceConfig(outboxinfra.ServiceActiveScheduler)
if !ok {
return fmt.Errorf("resolve active-scheduler outbox config failed")
}
if err := db.Table(cfg.TableName).AutoMigrate(&coremodel.AgentOutboxMessage{}); err != nil {
return fmt.Errorf("auto migrate active-scheduler outbox table failed for %s (%s): %w", cfg.Name, cfg.TableName, err)
}
return nil
}
type runtimeDependencyTable struct {
Name string
Reason string
}
// ensureRuntimeDependencyTables 在服务启动期校验迁移期共享主库依赖。
//
// 职责边界:
// 1. 只检查表是否存在,不 AutoMigrate、不补列、不修改任何跨域表
// 2. 把 active-scheduler 运行时仍然需要的 task / schedule / agent / notification outbox 边界显式化;
// 3. 若部署顺序、库权限或表结构归属不满足,启动阶段直接 fail fast避免第一次 trigger 才反复重试。
func ensureRuntimeDependencyTables(db *gorm.DB) error {
if db == nil {
return fmt.Errorf("active-scheduler runtime dependency check failed: db is nil")
}
for _, table := range activeSchedulerRuntimeDependencyTables() {
if err := ensureTableExists(db, table); err != nil {
return err
}
}
return nil
}
// ensureTableExists 只做存在性探测。
//
// 职责边界:
// 1. 不负责判断字段是否兼容,字段级契约由拥有该表的服务迁移脚本保证;
// 2. 不负责自动修复缺失表,避免 active-scheduler 越权创建其它服务的数据模型;
// 3. 返回错误会阻止服务启动,让部署问题尽早显现。
func ensureTableExists(db *gorm.DB, table runtimeDependencyTable) error {
if table.Name == "" {
return fmt.Errorf("active-scheduler runtime dependency table name is empty: %s", table.Reason)
}
if db.Migrator().HasTable(table.Name) {
return nil
}
return fmt.Errorf("active-scheduler runtime dependency table missing: %s (%s)", table.Name, table.Reason)
}
// activeSchedulerRuntimeDependencyTables 列出迁移期运行仍需共享主库访问的外部表。
//
// 说明:
// 1. active-scheduler 自有表在 OpenDBFromConfig 内迁移,这里只放跨域依赖;
// 2. notification outbox 表名来自 service catalog避免和 outbox 多表路由配置漂移;
// 3. 后续切到 task/schedule/agent/notification RPC 或 read model 后,应从这里移除对应表依赖。
func activeSchedulerRuntimeDependencyTables() []runtimeDependencyTable {
notificationOutboxTable := "notification_outbox_messages"
if cfg, ok := outboxinfra.ResolveServiceConfig(outboxinfra.ServiceNotification); ok && cfg.TableName != "" {
notificationOutboxTable = cfg.TableName
}
return []runtimeDependencyTable{
{Name: "tasks", Reason: "dry-run 读取 task_pool 事实confirm 时锁定 task_pool 目标"},
{Name: "schedule_events", Reason: "dry-run 读取日程事实confirm 时写入正式日程事件"},
{Name: "schedules", Reason: "dry-run 读取节次占用confirm 时写入正式节次"},
{Name: "task_classes", Reason: "confirm create_makeup 时校验 task_item 归属"},
{Name: "task_items", Reason: "confirm create_makeup 时锁定 task_item 目标"},
{Name: "agent_chats", Reason: "trigger 生成 preview 后预建主动调度会话"},
{Name: "chat_histories", Reason: "trigger 生成 preview 后写入会话首屏消息"},
{Name: "agent_timeline_events", Reason: "trigger 生成 preview 后写入主动调度时间线卡片"},
{Name: notificationOutboxTable, Reason: "ShouldNotify=true 时投递 notification.feishu.requested 事件"},
}
}