Version: 0.9.71.dev.260504
后端:
1.阶段 5 task 服务边界落地
- 新增 cmd/task 与 services/task/{dao,rpc,sv},承载 task zrpc、tasks 表迁移和 task outbox 消费边界
- 新增 gateway/client/task、shared/contracts/task 和 task port,gateway /api/v1/task/* 切到 task zrpc client
- 将 task.urgency.promote.requested handler / relay / retry loop 迁入 cmd/task,单体 worker 不再消费 task outbox
- 保留单体 Agent 残留 task 查询的 publish-only 写入能力,避免迁移期 task 事件丢失
- active-scheduler task facts / due job scanner 切到 task RPC,并移除启动期 tasks 表依赖检查
- 更新阶段 5 文档,记录 task 切流点、旧实现保留、跨域 DB 依赖缩减和下一轮建议
- 补充 task rpc 示例配置
This commit is contained in:
@@ -2,64 +2,54 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/LoveLosita/smartflow/backend/model"
|
||||
"github.com/LoveLosita/smartflow/backend/respond"
|
||||
"github.com/LoveLosita/smartflow/backend/service"
|
||||
taskcontracts "github.com/LoveLosita/smartflow/backend/shared/contracts/task"
|
||||
"github.com/LoveLosita/smartflow/backend/shared/ports"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
const taskRequestTimeout = 6 * time.Second
|
||||
|
||||
type TaskHandler struct {
|
||||
// 伸出手:准备接住 Service
|
||||
svc *service.TaskService
|
||||
client ports.TaskCommandClient
|
||||
}
|
||||
|
||||
// NewTaskHandler 创建 TaskHandler 实例
|
||||
func NewTaskHandler(svc *service.TaskService) *TaskHandler {
|
||||
return &TaskHandler{
|
||||
svc: svc,
|
||||
}
|
||||
// NewTaskHandler 创建 task HTTP 门面。
|
||||
func NewTaskHandler(client ports.TaskCommandClient) *TaskHandler {
|
||||
return &TaskHandler{client: client}
|
||||
}
|
||||
|
||||
func (th *TaskHandler) AddTask(c *gin.Context) {
|
||||
//1. 绑定请求参数
|
||||
var req model.UserAddTaskRequest
|
||||
var req taskcontracts.AddTaskRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, respond.WrongParamType)
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
// 用户ID从上下文中获取
|
||||
userID := c.GetInt("user_id")
|
||||
//2. 调用 Service 层处理业务逻辑
|
||||
// 创建一个带 1 秒超时的上下文
|
||||
ctx, cancel := context.WithTimeout(c.Request.Context(), 1*time.Second)
|
||||
defer cancel() // 记得释放资源
|
||||
resp, err := th.svc.AddTask(ctx, &req, userID)
|
||||
req.UserID = c.GetInt("user_id")
|
||||
|
||||
ctx, cancel := context.WithTimeout(c.Request.Context(), taskRequestTimeout)
|
||||
defer cancel()
|
||||
resp, err := th.client.AddTask(ctx, req)
|
||||
if err != nil {
|
||||
respond.DealWithError(c, err)
|
||||
return
|
||||
}
|
||||
//3. 返回响应
|
||||
c.JSON(http.StatusOK, respond.RespWithData(respond.Ok, resp))
|
||||
}
|
||||
|
||||
func (th *TaskHandler) GetUserTasks(c *gin.Context) {
|
||||
// 用户ID从上下文中获取
|
||||
userID := c.GetInt("user_id")
|
||||
//2. 调用 Service 层处理业务逻辑
|
||||
// 创建一个带 1 秒超时的上下文
|
||||
ctx, cancel := context.WithTimeout(c.Request.Context(), 1*time.Second)
|
||||
defer cancel() // 记得释放资源
|
||||
resp, err := th.svc.GetUserTasks(ctx, userID)
|
||||
|
||||
ctx, cancel := context.WithTimeout(c.Request.Context(), taskRequestTimeout)
|
||||
defer cancel()
|
||||
resp, err := th.client.GetUserTasks(ctx, userID)
|
||||
if err != nil {
|
||||
respond.DealWithError(c, err)
|
||||
return
|
||||
}
|
||||
//3. 返回响应
|
||||
c.JSON(http.StatusOK, respond.RespWithData(respond.Ok, resp))
|
||||
}
|
||||
|
||||
@@ -67,163 +57,98 @@ func (th *TaskHandler) GetUserTasks(c *gin.Context) {
|
||||
//
|
||||
// 职责边界:
|
||||
// 1. 负责解析 ids 与读取鉴权上下文中的 user_id;
|
||||
// 2. 负责调用 Service 复用任务缓存读取链路;
|
||||
// 2. 负责调用 task 服务,不直接读取任务缓存或数据库;
|
||||
// 3. 不修改任务、不触发幂等中间件、不反写 NewAgent timeline 历史 payload。
|
||||
func (th *TaskHandler) BatchTaskStatus(c *gin.Context) {
|
||||
// 1. 绑定请求参数。ids 允许为空切片,表示前端当前没有需要 hydration 的任务卡片。
|
||||
var req model.BatchTaskStatusRequest
|
||||
var req taskcontracts.BatchTaskStatusRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, respond.WrongParamType)
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
req.UserID = c.GetInt("user_id")
|
||||
|
||||
// 2. 从鉴权上下文读取 user_id,Service 会继续用该 user_id 限定任务集合。
|
||||
userID := c.GetInt("user_id")
|
||||
|
||||
// 3. 设置短超时:该接口只读缓存/任务列表,避免异常情况下长时间占用连接。
|
||||
ctx, cancel := context.WithTimeout(c.Request.Context(), 1*time.Second)
|
||||
ctx, cancel := context.WithTimeout(c.Request.Context(), taskRequestTimeout)
|
||||
defer cancel()
|
||||
|
||||
// 4. 调用 Service 做 ID 归一化与当前状态查询。
|
||||
resp, err := th.svc.BatchTaskStatus(ctx, &req, userID)
|
||||
resp, err := th.client.BatchTaskStatus(ctx, req)
|
||||
if err != nil {
|
||||
respond.DealWithError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 5. 返回统一响应结构,items 为空时仍按 success 返回,便于前端无分支处理。
|
||||
c.JSON(http.StatusOK, respond.RespWithData(respond.Ok, resp))
|
||||
}
|
||||
|
||||
// CompleteTask 标记任务为已完成。
|
||||
//
|
||||
// 职责边界:
|
||||
// 1. 负责解析请求与读取 user_id;
|
||||
// 2. 负责调用 Service 执行业务;
|
||||
// 3. 不负责幂等校验(幂等由路由中间件处理)。
|
||||
func (th *TaskHandler) CompleteTask(c *gin.Context) {
|
||||
// 1. 绑定请求参数。
|
||||
var req model.UserCompleteTaskRequest
|
||||
var req taskcontracts.CompleteTaskRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, respond.WrongParamType)
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
req.UserID = c.GetInt("user_id")
|
||||
|
||||
// 2. 从鉴权上下文获取 user_id,保证只能操作自己的任务。
|
||||
userID := c.GetInt("user_id")
|
||||
|
||||
// 3. 设置短超时,避免该写接口长期占用连接。
|
||||
ctx, cancel := context.WithTimeout(c.Request.Context(), 1*time.Second)
|
||||
ctx, cancel := context.WithTimeout(c.Request.Context(), taskRequestTimeout)
|
||||
defer cancel()
|
||||
|
||||
// 4. 调用 Service 执行"标记完成"逻辑。
|
||||
resp, err := th.svc.CompleteTask(ctx, &req, userID)
|
||||
resp, err := th.client.CompleteTask(ctx, req)
|
||||
if err != nil {
|
||||
respond.DealWithError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 5. 返回统一响应结构。
|
||||
c.JSON(http.StatusOK, respond.RespWithData(respond.Ok, resp))
|
||||
}
|
||||
|
||||
// UndoCompleteTask 取消任务"已完成"勾选。
|
||||
//
|
||||
// 职责边界:
|
||||
// 1. 负责解析请求与读取 user_id;
|
||||
// 2. 负责调用 Service 执行业务恢复;
|
||||
// 3. 不负责"任务是否已完成"的业务判断(由 Service/DAO 负责)。
|
||||
// UndoCompleteTask 取消任务“已完成”勾选。
|
||||
func (th *TaskHandler) UndoCompleteTask(c *gin.Context) {
|
||||
// 1. 绑定请求参数。
|
||||
var req model.UserUndoCompleteTaskRequest
|
||||
var req taskcontracts.UndoCompleteTaskRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, respond.WrongParamType)
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
req.UserID = c.GetInt("user_id")
|
||||
|
||||
// 2. 从鉴权上下文读取 user_id,保证只操作当前用户任务。
|
||||
userID := c.GetInt("user_id")
|
||||
|
||||
// 3. 设置短超时,避免该写接口占用连接过久。
|
||||
ctx, cancel := context.WithTimeout(c.Request.Context(), 1*time.Second)
|
||||
ctx, cancel := context.WithTimeout(c.Request.Context(), taskRequestTimeout)
|
||||
defer cancel()
|
||||
|
||||
// 4. 调用 Service 执行"取消已完成勾选"逻辑。
|
||||
resp, err := th.svc.UndoCompleteTask(ctx, &req, userID)
|
||||
resp, err := th.client.UndoCompleteTask(ctx, req)
|
||||
if err != nil {
|
||||
respond.DealWithError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 5. 返回统一响应结构。
|
||||
c.JSON(http.StatusOK, respond.RespWithData(respond.Ok, resp))
|
||||
}
|
||||
|
||||
// UpdateTask 更新任务属性(部分更新)。
|
||||
//
|
||||
// 职责边界:
|
||||
// 1. 负责解析请求与读取 user_id;
|
||||
// 2. 负责调用 Service 执行业务;
|
||||
// 3. 不负责幂等校验(幂等由路由中间件处理)。
|
||||
func (th *TaskHandler) UpdateTask(c *gin.Context) {
|
||||
// 1. 绑定请求参数。
|
||||
var req model.UserUpdateTaskRequest
|
||||
var req taskcontracts.UpdateTaskRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, respond.WrongParamType)
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
req.UserID = c.GetInt("user_id")
|
||||
|
||||
// 2. 从鉴权上下文读取 user_id,保证只操作当前用户任务。
|
||||
userID := c.GetInt("user_id")
|
||||
|
||||
// 3. 设置短超时,避免该写接口占用连接过久。
|
||||
ctx, cancel := context.WithTimeout(c.Request.Context(), 1*time.Second)
|
||||
ctx, cancel := context.WithTimeout(c.Request.Context(), taskRequestTimeout)
|
||||
defer cancel()
|
||||
|
||||
// 4. 调用 Service 执行更新逻辑。
|
||||
resp, err := th.svc.UpdateTask(ctx, &req, userID)
|
||||
resp, err := th.client.UpdateTask(ctx, req)
|
||||
if err != nil {
|
||||
respond.DealWithError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 5. 返回统一响应结构。
|
||||
c.JSON(http.StatusOK, respond.RespWithData(respond.Ok, resp))
|
||||
}
|
||||
|
||||
// DeleteTask 永久删除指定任务。
|
||||
//
|
||||
// 职责边界:
|
||||
// 1. 负责解析请求与读取 user_id;
|
||||
// 2. 负责调用 Service 执行删除;
|
||||
// 3. 不负责幂等校验(幂等由路由中间件处理)。
|
||||
func (th *TaskHandler) DeleteTask(c *gin.Context) {
|
||||
// 1. 绑定请求参数。
|
||||
var req model.UserCompleteTaskRequest
|
||||
var req taskcontracts.DeleteTaskRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, respond.WrongParamType)
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
req.UserID = c.GetInt("user_id")
|
||||
|
||||
// 2. 从鉴权上下文读取 user_id,保证只操作当前用户任务。
|
||||
userID := c.GetInt("user_id")
|
||||
|
||||
// 3. 设置短超时,避免该写接口占用连接过久。
|
||||
ctx, cancel := context.WithTimeout(c.Request.Context(), 1*time.Second)
|
||||
ctx, cancel := context.WithTimeout(c.Request.Context(), taskRequestTimeout)
|
||||
defer cancel()
|
||||
|
||||
// 4. 调用 Service 执行删除逻辑。
|
||||
taskID, err := th.svc.DeleteTask(ctx, &req, userID)
|
||||
resp, err := th.client.DeleteTask(ctx, req)
|
||||
if err != nil {
|
||||
respond.DealWithError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 5. 返回统一响应结构。
|
||||
c.JSON(http.StatusOK, respond.RespWithData(respond.Ok, gin.H{"task_id": taskID}))
|
||||
c.JSON(http.StatusOK, respond.RespWithData(respond.Ok, resp))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user