Files
smartmate/backend/gateway/api/schedule.go
Losita 29b8cf0ada 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 依赖收缩情况写入基线
2026-05-04 22:33:38 +08:00

217 lines
6.9 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 api
import (
"context"
"net/http"
"strconv"
"time"
"github.com/LoveLosita/smartflow/backend/respond"
schedulecontracts "github.com/LoveLosita/smartflow/backend/shared/contracts/schedule"
"github.com/LoveLosita/smartflow/backend/shared/ports"
"github.com/gin-gonic/gin"
)
const scheduleRequestTimeout = 6 * time.Second
type ScheduleAPI struct {
scheduleClient ports.ScheduleCommandClient
}
func NewScheduleAPI(scheduleClient ports.ScheduleCommandClient) *ScheduleAPI {
return &ScheduleAPI{
scheduleClient: scheduleClient,
}
}
func (s *ScheduleAPI) GetUserTodaySchedule(c *gin.Context) {
// 1. 从请求上下文中获取用户ID
userID := c.GetInt("user_id")
//2.调用服务层方法获取用户当天的日程安排
ctx, cancel := context.WithTimeout(c.Request.Context(), scheduleRequestTimeout)
defer cancel() // 记得释放资源
todaySchedules, err := s.scheduleClient.GetUserTodaySchedule(ctx, userID)
if err != nil {
respond.DealWithError(c, err)
return
}
//3.返回日程安排数据给前端
c.JSON(http.StatusOK, respond.RespWithData(respond.Ok, todaySchedules))
}
func (s *ScheduleAPI) GetUserWeeklySchedule(c *gin.Context) {
// 1. 从请求上下文中获取用户ID
userID := c.GetInt("user_id")
// 2. 从查询参数中获取 week 参数
week, err := strconv.Atoi(c.Query("week"))
if err != nil {
c.JSON(http.StatusBadRequest, respond.WrongParamType)
return
}
//3.调用服务层方法获取用户当周的日程安排
ctx, cancel := context.WithTimeout(c.Request.Context(), scheduleRequestTimeout)
defer cancel() // 记得释放资源
weeklySchedules, err := s.scheduleClient.GetUserWeeklySchedule(ctx, userID, week)
if err != nil {
respond.DealWithError(c, err)
return
}
//4.返回日程安排数据给前端
c.JSON(http.StatusOK, respond.RespWithData(respond.Ok, weeklySchedules))
}
func (s *ScheduleAPI) DeleteScheduleEvent(c *gin.Context) {
// 1. 从请求上下文中获取用户ID
userID := c.GetInt("user_id")
// 2. 从请求体中获取要删除的日程事件信息
var req []schedulecontracts.UserDeleteScheduleEvent
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, respond.WrongParamType)
return
}
//3.调用服务层方法删除指定的日程事件
ctx, cancel := context.WithTimeout(c.Request.Context(), scheduleRequestTimeout)
defer cancel() // 记得释放资源
err := s.scheduleClient.DeleteScheduleEvent(ctx, schedulecontracts.DeleteScheduleEventsRequest{
UserID: userID,
Events: req,
})
if err != nil {
respond.DealWithError(c, err)
return
}
//4.返回删除成功的响应给前端
c.JSON(http.StatusOK, respond.Ok)
}
func (s *ScheduleAPI) GetUserRecentCompletedSchedules(c *gin.Context) {
// 1. 从请求上下文中获取用户ID以及其他查询参数如 index 和 limit
userID := c.GetInt("user_id")
index := c.Query("index")
limit := c.Query("limit")
intIndex, err := strconv.Atoi(index)
if err != nil {
c.JSON(http.StatusBadRequest, respond.WrongParamType)
return
}
intLimit, err := strconv.Atoi(limit)
if err != nil {
c.JSON(http.StatusBadRequest, respond.WrongParamType)
return
}
//2.调用服务层方法获取用户最近完成的日程事件
ctx, cancel := context.WithTimeout(c.Request.Context(), scheduleRequestTimeout)
defer cancel() // 记得释放资源
completedSchedules, err := s.scheduleClient.GetUserRecentCompletedSchedules(ctx, schedulecontracts.RecentCompletedRequest{
UserID: userID,
Index: intIndex,
Limit: intLimit,
})
if err != nil {
respond.DealWithError(c, err)
return
}
//3.返回数据给前端
c.JSON(http.StatusOK, respond.RespWithData(respond.Ok, completedSchedules))
}
func (s *ScheduleAPI) GetUserOngoingSchedule(c *gin.Context) {
// 1. 从请求上下文中获取用户ID
userID := c.GetInt("user_id")
//2.调用服务层方法获取用户正在进行的日程事件
ctx, cancel := context.WithTimeout(c.Request.Context(), scheduleRequestTimeout)
defer cancel() // 记得释放资源
ongoingSchedule, err := s.scheduleClient.GetUserOngoingSchedule(ctx, userID)
if err != nil {
respond.DealWithError(c, err)
return
}
//3.返回数据给前端
c.JSON(http.StatusOK, respond.RespWithData(respond.Ok, ongoingSchedule))
}
func (s *ScheduleAPI) UserRevocateTaskItemFromSchedule(c *gin.Context) {
// 1. 从请求上下文中获取用户ID
userID := c.GetInt("user_id")
// 2. 获取要撤销的任务块ID
eventID := c.Query("event_id")
intEventID, err := strconv.Atoi(eventID)
if err != nil {
c.JSON(http.StatusBadRequest, respond.WrongParamType)
return
}
//3.调用服务层方法撤销任务块的安排
ctx, cancel := context.WithTimeout(c.Request.Context(), scheduleRequestTimeout)
defer cancel()
err = s.scheduleClient.RevokeTaskItemFromSchedule(ctx, schedulecontracts.RevokeTaskItemRequest{
UserID: userID,
EventID: intEventID,
})
if err != nil {
respond.DealWithError(c, err)
return
}
//4.返回撤销成功的响应给前端
c.JSON(http.StatusOK, respond.Ok)
}
func (s *ScheduleAPI) SmartPlanning(c *gin.Context) {
// 1. 从请求上下文中获取用户ID
userID := c.GetInt("user_id")
// 2. 从请求参数中获取智能规划的 task_class_id
taskClassID := c.Query("task_class_id")
intTaskClassID, err := strconv.Atoi(taskClassID)
if err != nil {
c.JSON(http.StatusBadRequest, respond.WrongParamType)
return
}
//3.调用服务层方法进行智能规划
ctx, cancel := context.WithTimeout(c.Request.Context(), scheduleRequestTimeout)
defer cancel() // 记得释放资源
res, err := s.scheduleClient.SmartPlanning(ctx, schedulecontracts.SmartPlanningRequest{
UserID: userID,
TaskClassID: intTaskClassID,
})
if err != nil {
respond.DealWithError(c, err)
return
}
//4.返回智能规划成功的响应给前端
c.JSON(http.StatusOK, respond.RespWithData(respond.Ok, res))
}
// SmartPlanningMulti 处理“多任务类智能粗排”请求。
//
// 职责边界:
// 1. 只负责参数绑定、超时控制、错误透传;
// 2. 具体业务校验与排序策略由 service 层统一处理;
// 3. 保留已有单任务类接口,不与其互斥。
func (s *ScheduleAPI) SmartPlanningMulti(c *gin.Context) {
// 1. 从请求上下文中读取登录用户 ID。
userID := c.GetInt("user_id")
// 2. 绑定多任务类请求体。
var req struct {
TaskClassIDs []int `json:"task_class_ids" binding:"required,min=1,dive,min=1"`
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, respond.WrongParamType)
return
}
// 3. 调用服务层执行多任务类粗排。
ctx, cancel := context.WithTimeout(c.Request.Context(), scheduleRequestTimeout)
defer cancel()
res, err := s.scheduleClient.SmartPlanningMulti(ctx, schedulecontracts.SmartPlanningMultiRequest{
UserID: userID,
TaskClassIDs: req.TaskClassIDs,
})
if err != nil {
respond.DealWithError(c, err)
return
}
// 4. 返回成功响应。
c.JSON(http.StatusOK, respond.RespWithData(respond.Ok, res))
}