Files
smartmate/backend/services/agent/sv/task_class_rpc_adapter.go
Losita 3b6fca44a6 Version: 0.9.77.dev.260505
后端:
1.阶段 6 CP4/CP5 目录收口与共享边界纯化
- 将 backend 根目录收口为 services、client、gateway、cmd、shared 五个一级目录
- 收拢 bootstrap、inits、infra/kafka、infra/outbox、conv、respond、pkg、middleware,移除根目录旧实现与空目录
- 将 utils 下沉到 services/userauth/internal/auth,将 logic 下沉到 services/schedule/core/planning
- 将迁移期 runtime 桥接实现统一收拢到 services/runtime/{conv,dao,eventsvc,model},删除 shared/legacy 与未再被 import 的旧 service 实现
- 将 gateway/shared/respond 收口为 HTTP/Gin 错误写回适配,shared/respond 仅保留共享错误语义与状态映射
- 将 HTTP IdempotencyMiddleware 与 RateLimitMiddleware 收口到 gateway/middleware
- 将 GormCachePlugin 下沉到 shared/infra/gormcache,将共享 RateLimiter 下沉到 shared/infra/ratelimit,将 agent token budget 下沉到 services/agent/shared
- 删除 InitEino 兼容壳,收缩 cmd/internal/coreinit 仅保留旧组合壳残留域初始化语义
- 更新微服务迁移计划与桌面 checklist,补齐 CP4/CP5 当前切流点、目录终态与验证结果
- 完成 go test ./...、git diff --check 与最终真实 smoke;health、register/login、task/create+get、schedule/today、task-class/list、memory/items、agent chat/meta/timeline/context-stats 全部 200,SSE 合并结果为 CP5_OK 且 [DONE] 只有 1 个
2026-05-05 23:25:07 +08:00

122 lines
4.7 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 sv
import (
"context"
"encoding/json"
"errors"
"strings"
"time"
agenttools "github.com/LoveLosita/smartflow/backend/services/agent/tools"
"github.com/LoveLosita/smartflow/backend/services/runtime/model"
taskclasscontracts "github.com/LoveLosita/smartflow/backend/shared/contracts/taskclass"
)
const taskClassUpsertRPCTimeout = 6 * time.Second
// TaskClassUpsertRPCClient 描述 agent 写入任务类时依赖的 task-class RPC 最小能力。
//
// 职责边界:
// 1. 只覆盖 upsert_task_class 工具需要的新增/更新能力;
// 2. 不暴露 task-class DAO、事务细节或 schedule 迁移期直写语义;
// 3. 读取型能力由 schedule provider 的独立接口承载,避免接口膨胀。
type TaskClassUpsertRPCClient interface {
AddTaskClass(ctx context.Context, req taskclasscontracts.UpsertTaskClassRequest) (json.RawMessage, error)
UpdateTaskClass(ctx context.Context, req taskclasscontracts.UpsertTaskClassRequest) (json.RawMessage, error)
}
type taskClassRPCUpsertAdapter struct {
client TaskClassUpsertRPCClient
}
// NewTaskClassRPCUpsertFunc 把 task-class zrpc client 适配成 agent 工具写入函数。
//
// 职责边界:
// 1. 只替换 agent upsert_task_class 的 DAO 直连路径;
// 2. 入参仍复用 agent 工具层已标准化的 UserAddTaskClassRequest
// 3. client 为空时返回会失败的闭包,让工具层保留既有错误包装语义。
func NewTaskClassRPCUpsertFunc(client TaskClassUpsertRPCClient) func(userID int, input agenttools.TaskClassUpsertInput) (agenttools.TaskClassUpsertPersistResult, error) {
adapter := &taskClassRPCUpsertAdapter{client: client}
return adapter.UpsertTaskClass
}
// UpsertTaskClass 通过 task-class zrpc 新增或更新任务类,并返回稳定 task_class_id。
func (a *taskClassRPCUpsertAdapter) UpsertTaskClass(userID int, input agenttools.TaskClassUpsertInput) (agenttools.TaskClassUpsertPersistResult, error) {
if a == nil || a.client == nil {
return agenttools.TaskClassUpsertPersistResult{}, errors.New("task-class rpc client is nil")
}
req := taskClassUpsertInputToContract(userID, input)
ctx, cancel := context.WithTimeout(context.Background(), taskClassUpsertRPCTimeout)
defer cancel()
var raw json.RawMessage
var err error
created := input.ID == 0
// 调用目的:把 agent 工具产出的任务类写入 task-class 服务,避免 agent 继续直连 task_classes/task_items。
if created {
raw, err = a.client.AddTaskClass(ctx, req)
} else {
raw, err = a.client.UpdateTaskClass(ctx, req)
}
if err != nil {
return agenttools.TaskClassUpsertPersistResult{}, err
}
var resp taskclasscontracts.UpsertTaskClassResponse
if err := json.Unmarshal(raw, &resp); err != nil {
return agenttools.TaskClassUpsertPersistResult{}, err
}
if resp.TaskClassID <= 0 {
return agenttools.TaskClassUpsertPersistResult{}, errors.New("task-class rpc upsert returned invalid task_class_id")
}
return agenttools.TaskClassUpsertPersistResult{
TaskClassID: resp.TaskClassID,
Created: resp.Created,
}, nil
}
func taskClassUpsertInputToContract(userID int, input agenttools.TaskClassUpsertInput) taskclasscontracts.UpsertTaskClassRequest {
req := input.Request
items := make([]taskclasscontracts.UpsertTaskClassItemConfig, 0, len(req.Items))
for _, item := range req.Items {
items = append(items, taskclasscontracts.UpsertTaskClassItemConfig{
ID: item.ID,
Order: item.Order,
Content: strings.TrimSpace(item.Content),
EmbeddedTime: toTaskClassContractTargetTime(item.EmbeddedTime),
})
}
return taskclasscontracts.UpsertTaskClassRequest{
UserID: userID,
TaskClassID: input.ID,
Name: strings.TrimSpace(req.Name),
StartDate: strings.TrimSpace(req.StartDate),
EndDate: strings.TrimSpace(req.EndDate),
Mode: strings.TrimSpace(req.Mode),
SubjectType: strings.TrimSpace(req.SubjectType),
DifficultyLevel: strings.TrimSpace(req.DifficultyLevel),
CognitiveIntensity: strings.TrimSpace(req.CognitiveIntensity),
Config: taskclasscontracts.UpsertTaskClassConfig{
TotalSlots: req.Config.TotalSlots,
AllowFillerCourse: req.Config.AllowFillerCourse,
Strategy: strings.TrimSpace(req.Config.Strategy),
ExcludedSlots: append([]int(nil), req.Config.ExcludedSlots...),
ExcludedDaysOfWeek: append([]int(nil), req.Config.ExcludedDaysOfWeek...),
},
Items: items,
}
}
func toTaskClassContractTargetTime(value *model.TargetTime) *taskclasscontracts.TargetTime {
if value == nil {
return nil
}
return &taskclasscontracts.TargetTime{
Week: value.Week,
DayOfWeek: value.DayOfWeek,
SectionFrom: value.SectionFrom,
SectionTo: value.SectionTo,
}
}