后端: 1.task-class 服务边界落地 - 新增 cmd/task-class 独立进程入口,落地 services/task_class dao/rpc/sv - 新增 gateway/client/taskclass、shared/contracts/taskclass 和 shared/ports task-class port - 将 /api/v1/task-class/* HTTP 门面切到 task-class zrpc,gateway 只保留鉴权、幂等、参数绑定和响应透传 - 保留 task-class 迁移期直写 schedule_events / schedules 权限,维持 insert/apply 与 item 状态更新的本地事务语义 - 修复 task-class 删除已排入日程任务块时 schedules / schedule_events 的外键删除顺序 - 补充 taskClass.rpc 示例配置与阶段 5 文档基线、切流点、残留依赖和 smoke 记录 - 忽略根目录 .tmp 临时烟测产物
222 lines
7.8 KiB
Go
222 lines
7.8 KiB
Go
package rpc
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
|
|
"github.com/LoveLosita/smartflow/backend/model"
|
|
"github.com/LoveLosita/smartflow/backend/respond"
|
|
"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"
|
|
)
|
|
|
|
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)
|
|
}
|
|
err := h.svc.AddOrUpdateTaskClass(ctx, toModelTaskClassRequest(contractReq), contractReq.UserID, taskClassCreate, 0)
|
|
return jsonResponse(nil, 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)
|
|
}
|
|
err := h.svc.AddOrUpdateTaskClass(ctx, toModelTaskClassRequest(contractReq), contractReq.UserID, taskClassUpdate, contractReq.TaskClassID)
|
|
return jsonResponse(nil, 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,
|
|
}
|
|
}
|