Version: 0.9.70.dev.260504

后端:
1. 阶段 5 schedule 首刀服务化落地,新增 `cmd/schedule`、`services/schedule/{dao,rpc,sv,core}`、`gateway/client/schedule`、`shared/contracts/schedule` 和 schedule port
2. gateway `/api/v1/schedule/*` 切到 schedule zrpc client,HTTP 门面只保留鉴权、参数绑定、超时和轻量转发
3. active-scheduler 的 schedule facts、feedback 和 confirm apply 改为调用 schedule RPC adapter,减少对 `schedule_events`、`schedules`、`task_classes`、`task_items` 的跨域 DB 依赖
4. 单体聊天主动调度 rerun 的 schedule 读写链路切到 schedule RPC,迁移期仅保留 task facts 直读 Gorm
5. 为 schedule zrpc 补充 `Ping` 启动健康检查,并在 gateway client 与 active-scheduler adapter 初始化时校验服务可用
6. `cmd/schedule` 独立初始化 DB / Redis,只 AutoMigrate schedule 自有表,并显式检查迁移期 task / task-class 依赖表
7. 更新 active-scheduler 依赖表检查和 preview confirm apply 抽象,保留旧 Gorm 实现作为迁移期回退路径
8. 补充 `schedule.rpc` 示例配置和 schedule HTTP RPC 超时配置

文档:
1. 更新微服务迁移计划,将阶段 5 schedule 首刀进展、当前切流点、旧实现保留范围和 active-scheduler DB 依赖收缩情况写入基线
This commit is contained in:
Losita
2026-05-04 22:33:38 +08:00
parent 4d9a5c4d30
commit 29b8cf0ada
27 changed files with 4456 additions and 51 deletions

View File

@@ -0,0 +1,151 @@
package schedule
import "time"
const (
TargetTypeTaskPool = "task_pool"
TargetTypeTaskItem = "task_item"
TargetTypeScheduleEvent = "schedule_event"
)
// UserRequest 是 schedule 只按用户读取数据的通用请求。
//
// 职责边界:
// 1. 只承载鉴权后得到的 user_id
// 2. 不承载 token、角色或 HTTP 参数;
// 3. 业务校验由 schedule 服务内部完成。
type UserRequest struct {
UserID int `json:"user_id"`
}
type WeekRequest struct {
UserID int `json:"user_id"`
Week int `json:"week"`
}
type DeleteScheduleEventsRequest struct {
UserID int `json:"user_id"`
Events []UserDeleteScheduleEvent `json:"events"`
}
type UserDeleteScheduleEvent struct {
ID int `json:"id"`
DeleteCourse bool `json:"delete_course"`
DeleteEmbeddedTask bool `json:"delete_embedded_task"`
}
type RecentCompletedRequest struct {
UserID int `json:"user_id"`
Index int `json:"index"`
Limit int `json:"limit"`
}
type RevokeTaskItemRequest struct {
UserID int `json:"user_id"`
EventID int `json:"event_id"`
}
type SmartPlanningRequest struct {
UserID int `json:"user_id"`
TaskClassID int `json:"task_class_id"`
}
type SmartPlanningMultiRequest struct {
UserID int `json:"user_id"`
TaskClassIDs []int `json:"task_class_ids"`
}
// Slot 是跨进程表达日程原子节次的稳定契约。
type Slot struct {
Week int `json:"week"`
DayOfWeek int `json:"day_of_week"`
Section int `json:"section"`
StartAt time.Time `json:"start_at,omitempty"`
EndAt time.Time `json:"end_at,omitempty"`
}
type ScheduleEventFact struct {
ID int `json:"id"`
UserID int `json:"user_id"`
Title string `json:"title"`
SourceType string `json:"source_type"`
RelID int `json:"rel_id"`
IsDynamicTask bool `json:"is_dynamic_task"`
IsCompleted bool `json:"is_completed"`
Slots []Slot `json:"slots"`
TaskClassID int `json:"task_class_id"`
TaskItemID int `json:"task_item_id"`
CanBeShortened bool `json:"can_be_shortened"`
}
type ScheduleWindowFacts struct {
Events []ScheduleEventFact `json:"events"`
OccupiedSlots []Slot `json:"occupied_slots"`
FreeSlots []Slot `json:"free_slots"`
NextDynamicTask *ScheduleEventFact `json:"next_dynamic_task,omitempty"`
TargetAlreadyScheduled bool `json:"target_already_scheduled"`
}
type ScheduleWindowRequest struct {
UserID int `json:"user_id"`
TargetType string `json:"target_type"`
TargetID int `json:"target_id"`
WindowStart time.Time `json:"window_start"`
WindowEnd time.Time `json:"window_end"`
Now time.Time `json:"now"`
}
type FeedbackRequest struct {
UserID int `json:"user_id"`
FeedbackID string `json:"feedback_id"`
IdempotencyKey string `json:"idempotency_key"`
TargetType string `json:"target_type"`
TargetID int `json:"target_id"`
}
type FeedbackFact struct {
FeedbackID string `json:"feedback_id"`
Text string `json:"text"`
TargetKnown bool `json:"target_known"`
TargetEventID int `json:"target_event_id"`
TargetTaskItemID int `json:"target_task_item_id"`
TargetTitle string `json:"target_title"`
SubmittedAt time.Time `json:"submitted_at"`
}
type FeedbackResponse struct {
Feedback FeedbackFact `json:"feedback"`
Found bool `json:"found"`
}
type ApplyActiveScheduleRequest struct {
PreviewID string `json:"preview_id"`
ApplyID string `json:"apply_id"`
UserID int `json:"user_id"`
CandidateID string `json:"candidate_id"`
Changes []ApplyChange `json:"changes"`
RequestedAt time.Time `json:"requested_at"`
TraceID string `json:"trace_id"`
}
type ApplyChange struct {
ChangeID string `json:"change_id"`
ChangeType string `json:"change_type"`
TargetType string `json:"target_type"`
TargetID int `json:"target_id"`
ToSlot *SlotSpan `json:"to_slot,omitempty"`
DurationSections int `json:"duration_sections"`
Metadata map[string]string `json:"metadata,omitempty"`
}
type SlotSpan struct {
Start Slot `json:"start"`
End Slot `json:"end"`
DurationSections int `json:"duration_sections"`
}
type ApplyActiveScheduleResult struct {
ApplyID string `json:"apply_id"`
AppliedEventIDs []int `json:"applied_event_ids,omitempty"`
AppliedScheduleIDs []int `json:"applied_schedule_ids,omitempty"`
}

View File

@@ -0,0 +1,25 @@
package ports
import (
"context"
"encoding/json"
schedulecontracts "github.com/LoveLosita/smartflow/backend/shared/contracts/schedule"
)
// ScheduleCommandClient 是 gateway 调用 schedule 服务的最小能力集合。
//
// 职责边界:
// 1. 只覆盖当前 /api/v1/schedule HTTP 门面需要的能力;
// 2. 不暴露 schedule DAO、事务编排、粗排算法或 apply 状态机;
// 3. 复杂响应先以 JSON 透传,避免 gateway 复制 schedule 内部 DTO。
type ScheduleCommandClient interface {
GetUserTodaySchedule(ctx context.Context, userID int) (json.RawMessage, error)
GetUserWeeklySchedule(ctx context.Context, userID int, week int) (json.RawMessage, error)
DeleteScheduleEvent(ctx context.Context, req schedulecontracts.DeleteScheduleEventsRequest) error
GetUserRecentCompletedSchedules(ctx context.Context, req schedulecontracts.RecentCompletedRequest) (json.RawMessage, error)
GetUserOngoingSchedule(ctx context.Context, userID int) (json.RawMessage, error)
RevokeTaskItemFromSchedule(ctx context.Context, req schedulecontracts.RevokeTaskItemRequest) error
SmartPlanning(ctx context.Context, req schedulecontracts.SmartPlanningRequest) (json.RawMessage, error)
SmartPlanningMulti(ctx context.Context, req schedulecontracts.SmartPlanningMultiRequest) (json.RawMessage, error)
}