From ad463eb6a1d9ada785a1f0c1599f7929f7c0862b Mon Sep 17 00:00:00 2001 From: LoveLosita <2810873701@qq.com> Date: Wed, 22 Apr 2026 21:06:09 +0800 Subject: [PATCH] =?UTF-8?q?Version:=200.9.35.dev.260422=20=E5=90=8E?= =?UTF-8?q?=E7=AB=AF=EF=BC=9A=201.=20=E4=BB=BB=E5=8A=A1=E6=9F=A5=E8=AF=A2?= =?UTF-8?q?=E7=BB=9F=E4=B8=80=E7=B4=A7=E6=80=A5=E6=80=A7=E6=8F=90=E5=8D=87?= =?UTF-8?q?=E9=93=BE=E8=B7=AF=E2=80=94=E2=80=94LLM=20=E5=B7=A5=E5=85=B7?= =?UTF-8?q?=E4=B8=8E=E5=89=8D=E7=AB=AF=E5=85=B1=E4=BA=AB=E8=AF=BB=E6=97=B6?= =?UTF-8?q?=E6=B4=BE=E7=94=9F=20+=20outbox=20=E5=BC=82=E6=AD=A5=E8=90=BD?= =?UTF-8?q?=E5=BA=93=20-=20service/task.go=EF=BC=9AGetUserTasks=20?= =?UTF-8?q?=E4=B8=AD=E8=AF=BB=E6=97=B6=E6=8F=90=E5=8D=87=E9=80=BB=E8=BE=91?= =?UTF-8?q?=E6=8A=BD=E5=8F=96=E4=B8=BA=E7=8B=AC=E7=AB=8B=E6=96=B9=E6=B3=95?= =?UTF-8?q?=20GetTasksWithUrgencyPromotion=EF=BC=8C=E8=BF=94=E5=9B=9E=20[]?= =?UTF-8?q?model.Task=20=E4=BE=9B=E4=B8=A4=E8=B7=AF=E5=A4=8D=E7=94=A8=20-?= =?UTF-8?q?=20service/agentsvc/agent.go=EF=BC=9A=E6=96=B0=E5=A2=9E=20GetTa?= =?UTF-8?q?sksWithUrgencyPromotionFunc=20=E5=87=BD=E6=95=B0=E6=B3=A8?= =?UTF-8?q?=E5=85=A5=E5=AD=97=E6=AE=B5=20-=20service/agentsvc/agent=5Ftask?= =?UTF-8?q?=5Fquery.go=EF=BC=9AQueryTasksForTool=20=E4=BC=98=E5=85=88?= =?UTF-8?q?=E8=B5=B0=E7=BB=9F=E4=B8=80=E6=8F=90=E5=8D=87=E9=93=BE=E8=B7=AF?= =?UTF-8?q?=EF=BC=8C=E6=9C=AA=E6=B3=A8=E5=85=A5=E6=97=B6=E5=9B=9E=E9=80=80?= =?UTF-8?q?=E6=97=A7=20taskRepo=20=E7=9B=B4=E6=8E=A5=E8=AF=BB=E5=8F=96=20-?= =?UTF-8?q?=20service/agent=5Fbridge.go=EF=BC=9ANewAgentServiceWithSchedul?= =?UTF-8?q?e=20=E6=8E=A5=E6=94=B6=20TaskService=20=E5=B9=B6=E6=B3=A8?= =?UTF-8?q?=E5=85=A5=E6=8F=90=E5=8D=87=E5=87=BD=E6=95=B0=20-=20cmd/start.g?= =?UTF-8?q?o=EF=BC=9A=E5=90=AF=E5=8A=A8=E6=8E=A5=E7=BA=BF=E4=BC=A0?= =?UTF-8?q?=E5=85=A5=20taskSv=202.=20=E7=A7=BB=E9=99=A4=E6=9C=AA=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E4=BE=9D=E8=B5=96=20-=20go.mod=EF=BC=9A=E5=88=A0?= =?UTF-8?q?=E9=99=A4=20github.com/bytedance/mockey?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/cmd/start.go | 2 +- backend/go.mod | 1 - backend/service/agent_bridge.go | 6 +++ backend/service/agentsvc/agent.go | 6 +++ backend/service/agentsvc/agent_task_query.go | 46 +++++++++++++------- backend/service/task.go | 22 ++++++---- 6 files changed, 57 insertions(+), 26 deletions(-) diff --git a/backend/cmd/start.go b/backend/cmd/start.go index d431fdc..024c138 100644 --- a/backend/cmd/start.go +++ b/backend/cmd/start.go @@ -151,7 +151,7 @@ func Start() { courseService := service.NewCourseService(courseRepo, scheduleRepo) taskClassService := service.NewTaskClassService(taskClassRepo, cacheRepo, scheduleRepo, manager) scheduleService := service.NewScheduleService(scheduleRepo, userRepo, taskClassRepo, manager, cacheRepo) - agentService := service.NewAgentServiceWithSchedule(aiHub, agentRepo, taskRepo, cacheRepo, agentCacheRepo, eventBus, scheduleService) + agentService := service.NewAgentServiceWithSchedule(aiHub, agentRepo, taskRepo, cacheRepo, agentCacheRepo, eventBus, scheduleService, taskSv) // newAgent 依赖接线。 agentService.SetAgentStateStore(dao.NewAgentStateStoreAdapter(cacheRepo)) diff --git a/backend/go.mod b/backend/go.mod index ef947dc..f0fc632 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -23,7 +23,6 @@ require ( github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/buger/jsonparser v1.1.1 // indirect github.com/bytedance/gopkg v0.1.3 // indirect - github.com/bytedance/mockey v1.3.0 // indirect github.com/bytedance/sonic v1.15.0 // indirect github.com/bytedance/sonic/loader v0.5.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect diff --git a/backend/service/agent_bridge.go b/backend/service/agent_bridge.go index fdac1c9..e642f8c 100644 --- a/backend/service/agent_bridge.go +++ b/backend/service/agent_bridge.go @@ -36,6 +36,7 @@ func NewAgentServiceWithSchedule( agentRedis *dao.AgentCache, eventPublisher outboxinfra.EventPublisher, scheduleSvc *ScheduleService, + taskSvc *TaskService, ) *AgentService { svc := agentsvc.NewAgentService(aiHub, repo, taskRepo, cacheDAO, agentRedis, eventPublisher) @@ -46,5 +47,10 @@ func NewAgentServiceWithSchedule( svc.ResolvePlanningWindowFunc = scheduleSvc.ResolvePlanningWindowByTaskClasses } + // 注入任务紧急性提升依赖:复用 TaskService 的统一提升 + outbox 投递链路。 + if taskSvc != nil { + svc.GetTasksWithUrgencyPromotionFunc = taskSvc.GetTasksWithUrgencyPromotion + } + return svc } diff --git a/backend/service/agentsvc/agent.go b/backend/service/agentsvc/agent.go index 5cd2f6c..29ead6d 100644 --- a/backend/service/agentsvc/agent.go +++ b/backend/service/agentsvc/agent.go @@ -50,6 +50,12 @@ type AgentService struct { // 2. 该函数只做”窗口解析”,不负责粗排与混排计算。 ResolvePlanningWindowFunc func(ctx context.Context, userID int, taskClassIDs []int) (startWeek, startDay, endWeek, endDay int, err error) + // ── 任务紧急性提升依赖(函数注入,避免 service 包循环依赖)── + + // GetTasksWithUrgencyPromotionFunc 读取用户任务并应用读时紧急性提升 + 异步落库触发。 + // 未注入时,QueryTasksForTool 回退到旧逻辑(纯内存提升,不持久化)。 + GetTasksWithUrgencyPromotionFunc func(ctx context.Context, userID int) ([]model.Task, error) + // ── newAgent 依赖(由 cmd/start.go 通过 Set* 方法注入)── toolRegistry *newagenttools.ToolRegistry scheduleProvider newagentmodel.ScheduleStateProvider diff --git a/backend/service/agentsvc/agent_task_query.go b/backend/service/agentsvc/agent_task_query.go index be8f36e..045de48 100644 --- a/backend/service/agentsvc/agent_task_query.go +++ b/backend/service/agentsvc/agent_task_query.go @@ -13,31 +13,47 @@ import ( ) func (s *AgentService) QueryTasksForTool(ctx context.Context, req newagentmodel.TaskQueryRequest) ([]newagentmodel.TaskQueryTaskRecord, error) { - _ = ctx if req.UserID <= 0 { return nil, errors.New("invalid user_id in task query") } - if s.taskRepo == nil { - return nil, errors.New("task repository is nil") - } - tasks, err := s.taskRepo.GetTasksByUserID(req.UserID) - if err != nil { - if errors.Is(err, respond.UserTasksEmpty) { - return make([]newagentmodel.TaskQueryTaskRecord, 0), nil + var tasks []model.Task + var err error + + // 优先使用统一提升链路(含缓存读取 + 读时派生 + outbox 异步落库)。 + if s.GetTasksWithUrgencyPromotionFunc != nil { + tasks, err = s.GetTasksWithUrgencyPromotionFunc(ctx, req.UserID) + if err != nil { + if errors.Is(err, respond.UserTasksEmpty) { + return make([]newagentmodel.TaskQueryTaskRecord, 0), nil + } + return nil, err + } + } else { + // 回退:未注入时走旧的 taskRepo 直接读取(无缓存、无持久化)。 + if s.taskRepo == nil { + return nil, errors.New("task repository is nil") + } + tasks, err = s.taskRepo.GetTasksByUserID(req.UserID) + if err != nil { + if errors.Is(err, respond.UserTasksEmpty) { + return make([]newagentmodel.TaskQueryTaskRecord, 0), nil + } + return nil, err + } + now := time.Now() + for i := range tasks { + applyReadTimeUrgencyPromotion(&tasks[i], now) } - return nil, err } - now := time.Now() + // 过滤、排序、截断。 filtered := make([]model.Task, 0, len(tasks)) - for _, originalTask := range tasks { - currentTask := originalTask - applyReadTimeUrgencyPromotion(¤tTask, now) - if !taskMatchesQueryFilter(currentTask, req) { + for _, task := range tasks { + if !taskMatchesQueryFilter(task, req) { continue } - filtered = append(filtered, currentTask) + filtered = append(filtered, task) } sortTasksForQuery(filtered, req) diff --git a/backend/service/task.go b/backend/service/task.go index af1d088..53e8caf 100644 --- a/backend/service/task.go +++ b/backend/service/task.go @@ -168,21 +168,25 @@ func (ts *TaskService) UndoCompleteTask(ctx context.Context, req *model.UserUndo // 2. 真实平移由异步消费者条件更新 DB; // 3. DB 更新后由 cache_deleter 自动删缓存,下一次读取自然拿到新状态。 func (ts *TaskService) GetUserTasks(ctx context.Context, userID int) ([]model.GetUserTaskResp, error) { - // 1. 读取原始任务模型(缓存优先,DB 兜底)。 + derivedTasks, err := ts.GetTasksWithUrgencyPromotion(ctx, userID) + if err != nil { + return nil, err + } + return conv.ModelToGetUserTasksResp(derivedTasks), nil +} + +// GetTasksWithUrgencyPromotion 读取用户任务并应用读时紧急性提升 + 异步落库触发。 +// +// 统一入口,供前端查询(GetUserTasks)和 LLM 工具查询(QueryTasksForTool)复用。 +// 调用方不应假设 DB 已更新——持久化是异步的。 +func (ts *TaskService) GetTasksWithUrgencyPromotion(ctx context.Context, userID int) ([]model.Task, error) { rawTasks, err := ts.getRawUserTasks(ctx, userID) if err != nil { return nil, err } - - // 2. 读时派生:本次请求内把“已到线任务”映射到紧急象限,同时收集待异步落库任务 ID。 derivedTasks, duePromoteTaskIDs := deriveTaskUrgencyForRead(rawTasks, time.Now()) - - // 3. 非阻断触发异步平移事件:发布失败不影响本次查询返回。 ts.tryEnqueueTaskUrgencyPromote(ctx, userID, duePromoteTaskIDs) - - // 4. 最后统一走 conv 转 DTO,避免 API 层直接依赖内部模型。 - response := conv.ModelToGetUserTasksResp(derivedTasks) - return response, nil + return derivedTasks, nil } // getRawUserTasks 读取“原始任务模型”。