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
This commit is contained in:
118
backend/shared/contracts/activescheduler/types.go
Normal file
118
backend/shared/contracts/activescheduler/types.go
Normal file
@@ -0,0 +1,118 @@
|
||||
package activescheduler
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ActiveScheduleRequest 是 gateway 调用 active-scheduler 的通用触发请求。
|
||||
//
|
||||
// 职责边界:
|
||||
// 1. 只承载 dry-run / trigger / preview 三个入口共享的触发事实;
|
||||
// 2. user_id 由 gateway 从 JWT 上下文补齐,不信任前端传入;
|
||||
// 3. payload 保留原始 JSON,由服务侧按 trigger_type 再解释,避免 gateway 承担领域解析。
|
||||
type ActiveScheduleRequest struct {
|
||||
UserID int `json:"user_id,omitempty"`
|
||||
TriggerType string `json:"trigger_type" binding:"required"`
|
||||
TargetType string `json:"target_type" binding:"required"`
|
||||
TargetID int `json:"target_id"`
|
||||
FeedbackID string `json:"feedback_id,omitempty"`
|
||||
IdempotencyKey string `json:"idempotency_key,omitempty"`
|
||||
MockNow *time.Time `json:"mock_now,omitempty"`
|
||||
Payload json.RawMessage `json:"payload,omitempty"`
|
||||
}
|
||||
|
||||
// TriggerResponse 是正式触发写入后的跨进程响应。
|
||||
type TriggerResponse struct {
|
||||
TriggerID string `json:"trigger_id"`
|
||||
Status string `json:"status"`
|
||||
PreviewID *string `json:"preview_id,omitempty"`
|
||||
DedupeHit bool `json:"dedupe_hit"`
|
||||
TraceID string `json:"trace_id,omitempty"`
|
||||
}
|
||||
|
||||
// GetPreviewRequest 是查询主动调度预览详情的跨进程请求。
|
||||
type GetPreviewRequest struct {
|
||||
UserID int `json:"user_id,omitempty"`
|
||||
PreviewID string `json:"preview_id"`
|
||||
}
|
||||
|
||||
// ConfirmPreviewRequest 是确认主动调度预览的跨进程请求。
|
||||
//
|
||||
// 职责边界:
|
||||
// 1. edited_changes 保留原始 JSON,由 active-scheduler 服务侧反序列化为 apply.Change;
|
||||
// 2. gateway 只补 user_id / preview_id,不理解正式写库命令;
|
||||
// 3. action / idempotency_key 继续保持现有前端请求语义。
|
||||
type ConfirmPreviewRequest struct {
|
||||
PreviewID string `json:"preview_id,omitempty"`
|
||||
UserID int `json:"user_id,omitempty"`
|
||||
CandidateID string `json:"candidate_id"`
|
||||
Action string `json:"action"`
|
||||
EditedChanges json.RawMessage `json:"edited_changes,omitempty"`
|
||||
IdempotencyKey string `json:"idempotency_key"`
|
||||
RequestedAt time.Time `json:"requested_at,omitempty"`
|
||||
TraceID string `json:"trace_id,omitempty"`
|
||||
}
|
||||
|
||||
type ApplyErrorCode string
|
||||
|
||||
const (
|
||||
ApplyErrorCodeExpired ApplyErrorCode = "expired"
|
||||
ApplyErrorCodeIdempotencyConflict ApplyErrorCode = "idempotency_conflict"
|
||||
ApplyErrorCodeBaseVersionChanged ApplyErrorCode = "base_version_changed"
|
||||
ApplyErrorCodeTargetNotFound ApplyErrorCode = "target_not_found"
|
||||
ApplyErrorCodeTargetCompleted ApplyErrorCode = "target_completed"
|
||||
ApplyErrorCodeTargetAlreadySchedule ApplyErrorCode = "target_already_scheduled"
|
||||
ApplyErrorCodeSlotConflict ApplyErrorCode = "slot_conflict"
|
||||
ApplyErrorCodeInvalidEditedChanges ApplyErrorCode = "invalid_edited_changes"
|
||||
ApplyErrorCodeUnsupportedChangeType ApplyErrorCode = "unsupported_change_type"
|
||||
ApplyErrorCodeDBError ApplyErrorCode = "db_error"
|
||||
ApplyErrorCodeInvalidRequest ApplyErrorCode = "invalid_request"
|
||||
ApplyErrorCodeForbidden ApplyErrorCode = "forbidden"
|
||||
ApplyErrorCodeAlreadyApplied ApplyErrorCode = "already_applied"
|
||||
)
|
||||
|
||||
const (
|
||||
ApplyStatusRejected = "rejected"
|
||||
ApplyStatusExpired = "expired"
|
||||
ApplyStatusFailed = "failed"
|
||||
)
|
||||
|
||||
// ApplyError 是 active-scheduler confirm/apply 业务拒绝的跨进程错误。
|
||||
//
|
||||
// 职责边界:
|
||||
// 1. 只承载可映射到 HTTP 4xx/5xx 的错误码与展示文案;
|
||||
// 2. 不暴露服务内部 applyadapter、DAO 或事务错误类型;
|
||||
// 3. cause 仅用于 gateway 日志串联,响应体只使用 code/message。
|
||||
type ApplyError struct {
|
||||
Code ApplyErrorCode
|
||||
Message string
|
||||
Cause error
|
||||
}
|
||||
|
||||
func (e *ApplyError) Error() string {
|
||||
if e == nil {
|
||||
return ""
|
||||
}
|
||||
message := strings.TrimSpace(e.Message)
|
||||
if message == "" {
|
||||
return string(e.Code)
|
||||
}
|
||||
return fmt.Sprintf("%s: %s", e.Code, message)
|
||||
}
|
||||
|
||||
func (e *ApplyError) Unwrap() error {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return e.Cause
|
||||
}
|
||||
|
||||
// ConfirmErrorResult 是 confirm/apply 失败时仍返回给前端的稳定数据形状。
|
||||
type ConfirmErrorResult struct {
|
||||
ApplyStatus string `json:"apply_status"`
|
||||
ErrorCode ApplyErrorCode `json:"error_code,omitempty"`
|
||||
ErrorMessage string `json:"error_message,omitempty"`
|
||||
}
|
||||
22
backend/shared/ports/active_scheduler.go
Normal file
22
backend/shared/ports/active_scheduler.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package ports
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
contracts "github.com/LoveLosita/smartflow/backend/shared/contracts/activescheduler"
|
||||
)
|
||||
|
||||
// ActiveSchedulerCommandClient 是 gateway 调用 active-scheduler 服务的最小能力集合。
|
||||
//
|
||||
// 职责边界:
|
||||
// 1. 只覆盖当前 HTTP 入口需要的 dry-run / trigger / preview / confirm;
|
||||
// 2. 不暴露 active-scheduler 的 DAO、graph、selection、job scanner 或 outbox consumer;
|
||||
// 3. 复杂响应先以原始 JSON 透传,避免 gateway 重建一套主动调度 DTO。
|
||||
type ActiveSchedulerCommandClient interface {
|
||||
DryRun(ctx context.Context, req contracts.ActiveScheduleRequest) (json.RawMessage, error)
|
||||
Trigger(ctx context.Context, req contracts.ActiveScheduleRequest) (*contracts.TriggerResponse, error)
|
||||
CreatePreview(ctx context.Context, req contracts.ActiveScheduleRequest) (json.RawMessage, error)
|
||||
GetPreview(ctx context.Context, req contracts.GetPreviewRequest) (json.RawMessage, error)
|
||||
ConfirmPreview(ctx context.Context, req contracts.ConfirmPreviewRequest) (json.RawMessage, error)
|
||||
}
|
||||
Reference in New Issue
Block a user