feat: ✅ 新增获取完整任务类与修改任务类接口并完成测试 - 新增获取完整任务类接口 📋 - 实现创建任务类的逆向数据转换函数 🔄 - 工程量较大,涉及完整数据结构还原 🏗️ - 新增修改任务类接口 ✏️ - 调整 service 层 AddOrUpdateTaskClass 函数逻辑 - 复用创建任务类的大部分实现,并通过 method 区分创建/更新操作 ♻️ - 更新 dao 层操作逻辑 🗄️ - 增加防止越权修改其它用户任务类的机制 🔒 - 两个接口代码量巨大,但均已测试通过 🧪💪
169 lines
4.4 KiB
Go
169 lines
4.4 KiB
Go
package conv
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"time"
|
|
|
|
"github.com/LoveLosita/smartflow/backend/model"
|
|
"github.com/LoveLosita/smartflow/backend/respond"
|
|
)
|
|
|
|
const dateLayout = "2006-01-02"
|
|
|
|
func parseDatePtr(s string) (*time.Time, error) {
|
|
if s == "" {
|
|
return nil, nil
|
|
}
|
|
t, err := time.ParseInLocation(dateLayout, s, time.Local)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &t, nil
|
|
}
|
|
|
|
func ProcessUserAddTaskClassRequest(req *model.UserAddTaskClassRequest, userID int) (*model.TaskClass, []model.TaskClassItem, error) {
|
|
startDate, err := parseDatePtr(req.StartDate)
|
|
if err != nil {
|
|
return nil, nil, respond.WrongParamType
|
|
}
|
|
endDate, err := parseDatePtr(req.EndDate)
|
|
if err != nil {
|
|
return nil, nil, respond.WrongParamType
|
|
}
|
|
//1.填充section1,2
|
|
taskClass := model.TaskClass{
|
|
Name: &req.Name,
|
|
Mode: &req.Mode,
|
|
StartDate: startDate,
|
|
EndDate: endDate,
|
|
UserID: &userID,
|
|
}
|
|
//2.填充section3
|
|
taskClass.TotalSlots = &req.Config.TotalSlots
|
|
taskClass.AllowFillerCourse = &req.Config.AllowFillerCourse
|
|
taskClass.Strategy = &req.Config.Strategy
|
|
//处理 ExcludedSlots 切片为 JSON 字符串
|
|
if len(req.Config.ExcludedSlots) > 0 {
|
|
//转换为 JSON 字符串
|
|
excludedSlotsJSON := "["
|
|
for i, slot := range req.Config.ExcludedSlots {
|
|
excludedSlotsJSON += string(rune(slot + '0')) //简单转换为字符
|
|
if i != len(req.Config.ExcludedSlots)-1 {
|
|
excludedSlotsJSON += ","
|
|
}
|
|
}
|
|
excludedSlotsJSON += "]"
|
|
taskClass.ExcludedSlots = &excludedSlotsJSON
|
|
} else {
|
|
emptyJSON := "[]"
|
|
taskClass.ExcludedSlots = &emptyJSON
|
|
}
|
|
//3.开始构建 items
|
|
var items []model.TaskClassItem
|
|
for _, itemReq := range req.Items {
|
|
item := model.TaskClassItem{ //填充section 2
|
|
Order: &itemReq.Order,
|
|
Content: &itemReq.Content,
|
|
EmbeddedTime: itemReq.EmbeddedTime,
|
|
Status: nil,
|
|
}
|
|
items = append(items, item)
|
|
}
|
|
return &taskClass, items, nil
|
|
}
|
|
|
|
func timeOrZero(t *time.Time) time.Time {
|
|
if t == nil {
|
|
return time.Time{}
|
|
}
|
|
return *t
|
|
}
|
|
|
|
func TaskClassModelToResponse(taskClasses []model.TaskClass) *model.UserGetTaskClassesResponse {
|
|
var resp model.UserGetTaskClassesResponse
|
|
for _, tc := range taskClasses {
|
|
tcResp := model.TaskClassSummary{
|
|
ID: tc.ID,
|
|
Name: *tc.Name,
|
|
Mode: *tc.Mode,
|
|
StartDate: timeOrZero(tc.StartDate),
|
|
EndDate: timeOrZero(tc.EndDate),
|
|
TotalSlots: *tc.TotalSlots,
|
|
Strategy: *tc.Strategy,
|
|
}
|
|
resp.TaskClasses = append(resp.TaskClasses, tcResp)
|
|
}
|
|
return &resp
|
|
}
|
|
|
|
func ProcessUserGetCompleteTaskClassRequest(taskClass *model.TaskClass) (*model.UserAddTaskClassRequest, error) {
|
|
if taskClass == nil {
|
|
return nil, errors.New("源数据对象不可为空")
|
|
}
|
|
// 1. 映射基础信息 (处理指针解引用)
|
|
req := &model.UserAddTaskClassRequest{
|
|
Name: safeStr(taskClass.Name),
|
|
Mode: safeStr(taskClass.Mode),
|
|
StartDate: formatTime(taskClass.StartDate),
|
|
EndDate: formatTime(taskClass.EndDate),
|
|
}
|
|
// 2. 映射配置信息 (Config Section)
|
|
req.Config = model.UserAddTaskClassConfig{
|
|
TotalSlots: safeInt(taskClass.TotalSlots),
|
|
AllowFillerCourse: safeBool(taskClass.AllowFillerCourse),
|
|
Strategy: safeStr(taskClass.Strategy),
|
|
}
|
|
// 3. 处理 ExcludedSlots JSON 字符串 -> []int
|
|
if taskClass.ExcludedSlots != nil && *taskClass.ExcludedSlots != "" {
|
|
var excluded []int
|
|
// 直接使用标准反序列化,比手动处理 rune 字符要健壮得多
|
|
if err := json.Unmarshal([]byte(*taskClass.ExcludedSlots), &excluded); err == nil {
|
|
req.Config.ExcludedSlots = excluded
|
|
}
|
|
}
|
|
// 4. 映射子项信息 (Items Section)
|
|
// 此时 items 已经通过 Preload 加载到了 taskClass.Items 中
|
|
req.Items = make([]model.UserAddTaskClassItemRequest, 0, len(taskClass.Items))
|
|
for _, item := range taskClass.Items {
|
|
itemReq := model.UserAddTaskClassItemRequest{
|
|
Order: safeInt(item.Order),
|
|
Content: safeStr(item.Content),
|
|
EmbeddedTime: item.EmbeddedTime, // 结构体指针直接复用
|
|
}
|
|
req.Items = append(req.Items, itemReq)
|
|
}
|
|
return req, nil
|
|
}
|
|
|
|
// --- 🛡️ 辅助工具函数:保持代码清爽并防止 Panic ---
|
|
|
|
func safeStr(s *string) string {
|
|
if s == nil {
|
|
return ""
|
|
}
|
|
return *s
|
|
}
|
|
|
|
func safeInt(i *int) int {
|
|
if i == nil {
|
|
return 0
|
|
}
|
|
return *i
|
|
}
|
|
|
|
func safeBool(b *bool) bool {
|
|
if b == nil {
|
|
return true
|
|
}
|
|
return *b
|
|
}
|
|
|
|
func formatTime(t *time.Time) string {
|
|
if t == nil {
|
|
return ""
|
|
}
|
|
// 务必使用 2006-01-02 格式以匹配前端校验
|
|
return t.Format("2006-01-02")
|
|
}
|