Files
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

234 lines
8.4 KiB
Go

package rpc
import (
"context"
"encoding/json"
"errors"
"github.com/LoveLosita/smartflow/backend/services/runtime/model"
"github.com/LoveLosita/smartflow/backend/services/task_class/rpc/pb"
taskclasssv "github.com/LoveLosita/smartflow/backend/services/task_class/sv"
taskclasscontracts "github.com/LoveLosita/smartflow/backend/shared/contracts/taskclass"
"github.com/LoveLosita/smartflow/backend/shared/respond"
)
const (
taskClassCreate = 0
taskClassUpdate = 1
)
type Handler struct {
pb.UnimplementedTaskClassServer
svc *taskclasssv.TaskClassService
}
func NewHandler(svc *taskclasssv.TaskClassService) *Handler {
return &Handler{svc: svc}
}
// Ping 供调用方在启动期确认 task-class zrpc 已可用。
func (h *Handler) Ping(ctx context.Context, req *pb.StatusResponse) (*pb.StatusResponse, error) {
if err := h.ensureReady(req); err != nil {
return nil, err
}
return &pb.StatusResponse{}, nil
}
func (h *Handler) AddTaskClass(ctx context.Context, req *pb.JSONRequest) (*pb.JSONResponse, error) {
if err := h.ensureReady(req); err != nil {
return nil, err
}
var contractReq taskclasscontracts.UpsertTaskClassRequest
if err := json.Unmarshal(req.PayloadJson, &contractReq); err != nil {
return nil, grpcErrorFromServiceError(respond.WrongParamType)
}
taskClassID, err := h.svc.AddOrUpdateTaskClass(ctx, toModelTaskClassRequest(contractReq), contractReq.UserID, taskClassCreate, 0)
return jsonResponse(taskclasscontracts.UpsertTaskClassResponse{TaskClassID: taskClassID, Created: true}, err)
}
func (h *Handler) ListTaskClasses(ctx context.Context, req *pb.JSONRequest) (*pb.JSONResponse, error) {
if err := h.ensureReady(req); err != nil {
return nil, err
}
var contractReq taskclasscontracts.UserRequest
if err := json.Unmarshal(req.PayloadJson, &contractReq); err != nil {
return nil, grpcErrorFromServiceError(respond.WrongParamType)
}
data, err := h.svc.GetUserTaskClassInfos(ctx, contractReq.UserID)
return jsonResponse(data, err)
}
func (h *Handler) GetTaskClass(ctx context.Context, req *pb.JSONRequest) (*pb.JSONResponse, error) {
if err := h.ensureReady(req); err != nil {
return nil, err
}
var contractReq taskclasscontracts.GetTaskClassRequest
if err := json.Unmarshal(req.PayloadJson, &contractReq); err != nil {
return nil, grpcErrorFromServiceError(respond.WrongParamType)
}
data, err := h.svc.GetUserCompleteTaskClass(ctx, contractReq.UserID, contractReq.TaskClassID)
return jsonResponse(data, err)
}
func (h *Handler) UpdateTaskClass(ctx context.Context, req *pb.JSONRequest) (*pb.JSONResponse, error) {
if err := h.ensureReady(req); err != nil {
return nil, err
}
var contractReq taskclasscontracts.UpsertTaskClassRequest
if err := json.Unmarshal(req.PayloadJson, &contractReq); err != nil {
return nil, grpcErrorFromServiceError(respond.WrongParamType)
}
taskClassID, err := h.svc.AddOrUpdateTaskClass(ctx, toModelTaskClassRequest(contractReq), contractReq.UserID, taskClassUpdate, contractReq.TaskClassID)
return jsonResponse(taskclasscontracts.UpsertTaskClassResponse{TaskClassID: taskClassID, Created: false}, err)
}
func (h *Handler) GetAgentTaskClasses(ctx context.Context, req *pb.JSONRequest) (*pb.JSONResponse, error) {
if err := h.ensureReady(req); err != nil {
return nil, err
}
var contractReq taskclasscontracts.AgentTaskClassesRequest
if err := json.Unmarshal(req.PayloadJson, &contractReq); err != nil {
return nil, grpcErrorFromServiceError(respond.WrongParamType)
}
data, err := h.svc.GetAgentTaskClasses(ctx, contractReq)
return jsonResponse(data, err)
}
func (h *Handler) InsertTaskClassItemIntoSchedule(ctx context.Context, req *pb.JSONRequest) (*pb.JSONResponse, error) {
if err := h.ensureReady(req); err != nil {
return nil, err
}
var contractReq taskclasscontracts.InsertTaskClassItemIntoScheduleRequest
if err := json.Unmarshal(req.PayloadJson, &contractReq); err != nil {
return nil, grpcErrorFromServiceError(respond.WrongParamType)
}
err := h.svc.AddTaskClassItemIntoSchedule(ctx, toModelInsertTaskClassItemRequest(contractReq), contractReq.UserID, contractReq.TaskItemID)
return jsonResponse(nil, err)
}
func (h *Handler) DeleteTaskClassItem(ctx context.Context, req *pb.JSONRequest) (*pb.JSONResponse, error) {
if err := h.ensureReady(req); err != nil {
return nil, err
}
var contractReq taskclasscontracts.DeleteTaskClassItemRequest
if err := json.Unmarshal(req.PayloadJson, &contractReq); err != nil {
return nil, grpcErrorFromServiceError(respond.WrongParamType)
}
err := h.svc.DeleteTaskClassItem(ctx, contractReq.UserID, contractReq.TaskItemID)
return jsonResponse(nil, err)
}
func (h *Handler) DeleteTaskClass(ctx context.Context, req *pb.JSONRequest) (*pb.JSONResponse, error) {
if err := h.ensureReady(req); err != nil {
return nil, err
}
var contractReq taskclasscontracts.DeleteTaskClassRequest
if err := json.Unmarshal(req.PayloadJson, &contractReq); err != nil {
return nil, grpcErrorFromServiceError(respond.WrongParamType)
}
err := h.svc.DeleteTaskClass(ctx, contractReq.UserID, contractReq.TaskClassID)
return jsonResponse(nil, err)
}
func (h *Handler) ApplyBatchIntoSchedule(ctx context.Context, req *pb.JSONRequest) (*pb.JSONResponse, error) {
if err := h.ensureReady(req); err != nil {
return nil, err
}
var contractReq taskclasscontracts.ApplyBatchIntoScheduleRequest
if err := json.Unmarshal(req.PayloadJson, &contractReq); err != nil {
return nil, grpcErrorFromServiceError(respond.WrongParamType)
}
err := h.svc.BatchApplyPlans(ctx, contractReq.TaskClassID, contractReq.UserID, toModelBatchRequest(contractReq))
return jsonResponse(nil, err)
}
func (h *Handler) ensureReady(req any) error {
if h == nil || h.svc == nil {
return grpcErrorFromServiceError(errors.New("task-class service dependency not initialized"))
}
if req == nil {
return grpcErrorFromServiceError(respond.MissingParam)
}
return nil
}
func jsonResponse(value any, err error) (*pb.JSONResponse, error) {
if err != nil {
return nil, grpcErrorFromServiceError(err)
}
raw, err := json.Marshal(value)
if err != nil {
return nil, grpcErrorFromServiceError(err)
}
return &pb.JSONResponse{DataJson: raw}, nil
}
func toModelTaskClassRequest(req taskclasscontracts.UpsertTaskClassRequest) *model.UserAddTaskClassRequest {
items := make([]model.UserAddTaskClassItemRequest, 0, len(req.Items))
for _, item := range req.Items {
items = append(items, model.UserAddTaskClassItemRequest{
ID: item.ID,
Order: item.Order,
Content: item.Content,
EmbeddedTime: toModelTargetTime(item.EmbeddedTime),
})
}
return &model.UserAddTaskClassRequest{
Name: req.Name,
StartDate: req.StartDate,
EndDate: req.EndDate,
Mode: req.Mode,
SubjectType: req.SubjectType,
DifficultyLevel: req.DifficultyLevel,
CognitiveIntensity: req.CognitiveIntensity,
Config: model.UserAddTaskClassConfig{
TotalSlots: req.Config.TotalSlots,
AllowFillerCourse: req.Config.AllowFillerCourse,
Strategy: req.Config.Strategy,
ExcludedSlots: append([]int(nil), req.Config.ExcludedSlots...),
ExcludedDaysOfWeek: append([]int(nil), req.Config.ExcludedDaysOfWeek...),
},
Items: items,
}
}
func toModelTargetTime(value *taskclasscontracts.TargetTime) *model.TargetTime {
if value == nil {
return nil
}
return &model.TargetTime{
Week: value.Week,
DayOfWeek: value.DayOfWeek,
SectionFrom: value.SectionFrom,
SectionTo: value.SectionTo,
}
}
func toModelInsertTaskClassItemRequest(req taskclasscontracts.InsertTaskClassItemIntoScheduleRequest) *model.UserInsertTaskClassItemToScheduleRequest {
return &model.UserInsertTaskClassItemToScheduleRequest{
Week: req.Week,
DayOfWeek: req.DayOfWeek,
StartSection: req.StartSection,
EndSection: req.EndSection,
EmbedCourseEventID: req.EmbedCourseEventID,
}
}
func toModelBatchRequest(req taskclasscontracts.ApplyBatchIntoScheduleRequest) *model.UserInsertTaskClassItemToScheduleRequestBatch {
items := make([]model.SingleTaskClassItem, 0, len(req.Items))
for _, item := range req.Items {
items = append(items, model.SingleTaskClassItem{
TaskItemID: item.TaskItemID,
Week: item.Week,
DayOfWeek: item.DayOfWeek,
StartSection: item.StartSection,
EndSection: item.EndSection,
EmbedCourseEventID: item.EmbedCourseEventID,
})
}
return &model.UserInsertTaskClassItemToScheduleRequestBatch{
TaskClassID: req.TaskClassID,
Items: items,
}
}