完成课程资源
This commit is contained in:
@@ -6,6 +6,8 @@ package course_content
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/JACKYMYPERSON/hldrCenter/config"
|
||||
"github.com/JACKYMYPERSON/hldrCenter/internal/course_content/internal/logic/course_content"
|
||||
@@ -18,10 +20,18 @@ import (
|
||||
func DeleteContentHandler(cfg *config.Config) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.DeleteContentReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
pathParts := strings.Split(r.URL.Path, "/")
|
||||
if len(pathParts) < 3 { // 确保路径格式正确
|
||||
httpx.ErrorCtx(r.Context(), w, fmt.Errorf("invalid path format"))
|
||||
return
|
||||
}
|
||||
idStr := pathParts[3]
|
||||
id, err := strconv.ParseInt(idStr, 10, 64)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, fmt.Errorf("invalid meeting ID"))
|
||||
return
|
||||
}
|
||||
req.Id = int(id)
|
||||
mysqlCfg := cfg.MySQL
|
||||
dsn := fmt.Sprintf(
|
||||
"%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=true&loc=Local",
|
||||
|
||||
@@ -6,6 +6,8 @@ package course_content
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/JACKYMYPERSON/hldrCenter/config"
|
||||
"github.com/JACKYMYPERSON/hldrCenter/internal/course_content/internal/logic/course_content"
|
||||
@@ -18,10 +20,18 @@ import (
|
||||
func GetContentHandler(cfg *config.Config) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.GetContentReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
pathParts := strings.Split(r.URL.Path, "/")
|
||||
if len(pathParts) < 3 { // 确保路径格式正确
|
||||
httpx.ErrorCtx(r.Context(), w, fmt.Errorf("invalid path format"))
|
||||
return
|
||||
}
|
||||
idStr := pathParts[3]
|
||||
id, err := strconv.ParseInt(idStr, 10, 64)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, fmt.Errorf("invalid meeting ID"))
|
||||
return
|
||||
}
|
||||
req.Id = int(id)
|
||||
|
||||
mysqlCfg := cfg.MySQL
|
||||
dsn := fmt.Sprintf(
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/JACKYMYPERSON/hldrCenter/config"
|
||||
"github.com/JACKYMYPERSON/hldrCenter/internal/course_content/internal/model"
|
||||
"github.com/JACKYMYPERSON/hldrCenter/internal/course_content/internal/types"
|
||||
"github.com/JACKYMYPERSON/hldrCenter/util"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
@@ -31,46 +32,109 @@ func NewGetContentListLogic(ctx context.Context, cfg *config.Config, model model
|
||||
}
|
||||
|
||||
func (l *GetContentListLogic) GetContentList(req *types.GetContentListReq) (resp *types.GetContentListResp, err error) {
|
||||
// 1. 参数校验:确保课程ID有效
|
||||
// 1. 参数校验(增强校验,确保CourseId必填)
|
||||
if req.CourseId <= 0 {
|
||||
return nil, fmt.Errorf("参数错误:课程ID必须为正整数")
|
||||
}
|
||||
// ParentId 可选,允许为0(表示章节)或其他正数(表示小节),此处仅校验非负
|
||||
if req.ParentId < 0 {
|
||||
return nil, fmt.Errorf("参数错误:父级ID不能为负数")
|
||||
}
|
||||
|
||||
// 2. 调用 Model 层查询符合条件的内容列表
|
||||
// 假设 Model 层有查询方法:根据 course_id 和 parent_id 筛选,按 sort 排序
|
||||
// 若 Model 层无此方法,可构造 SQL:SELECT * FROM course_content WHERE course_id=? AND parent_id=? ORDER BY sort
|
||||
contentModels, err := l.model.FindByCourseAndParent(l.ctx,
|
||||
int64(req.CourseId), int64(req.ParentId))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("查询课程内容列表失败:%w", err)
|
||||
}
|
||||
|
||||
// 3. 转换模型数据为响应结构体(适配前端需要的格式)
|
||||
// 2. 区分查询场景:parentId=0 查树形结构(章节+子章节),否则查平级内容
|
||||
var contentList []types.CourseContent
|
||||
for _, modelItem := range contentModels {
|
||||
contentList = append(contentList, types.CourseContent{
|
||||
Id: int(modelItem.Id), // 数据库ID可能为int64,转换为int
|
||||
CourseId: int(modelItem.CourseId), // 课程ID转换
|
||||
ParentId: int(modelItem.ParentId), // 父级ID转换
|
||||
Title: modelItem.Title,
|
||||
// 若数据库中Content是sql.NullString,需转换为普通string(复用工具函数)
|
||||
Content: NullStringToString(modelItem.Content),
|
||||
Sort: int(modelItem.Sort),
|
||||
})
|
||||
if req.ParentId == 0 {
|
||||
// 场景1:查询所有章节(parentId=0)及其子章节
|
||||
contentList, err = l.queryTreeContent(req.CourseId)
|
||||
} else {
|
||||
// 场景2:查询指定父级的平级内容(兼容原有逻辑)
|
||||
contentList, err = l.queryFlatContent(req.CourseId, req.ParentId)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("查询课程内容失败:%w", err)
|
||||
}
|
||||
|
||||
// 4. 构造响应(包含空列表的情况,前端可正常处理)
|
||||
// 3. 构造响应
|
||||
resp = &types.GetContentListResp{
|
||||
BaseResp: types.BaseResp{
|
||||
Code: 0,
|
||||
Message: "查询课程内容列表成功",
|
||||
},
|
||||
Data: contentList, // 即使为空列表,也正常返回(避免前端处理null的麻烦)
|
||||
Data: contentList,
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// queryTreeContent 查询树形结构:章节(parentId=0)+ 子章节
|
||||
func (l *GetContentListLogic) queryTreeContent(courseId int) ([]types.CourseContent, error) {
|
||||
// 第一步:查询所有章节(parentId=0),按sort升序
|
||||
chapterModels, err := l.model.FindByCourseAndParent(l.ctx, int64(courseId), 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 第二步:遍历每个章节,查询对应的子章节(parentId=章节ID)
|
||||
var treeList []types.CourseContent
|
||||
for _, chapter := range chapterModels {
|
||||
// 转换章节为响应格式
|
||||
respChapter := types.CourseContent{
|
||||
Id: int(chapter.Id),
|
||||
CourseId: int(chapter.CourseId),
|
||||
ParentId: int(chapter.ParentId),
|
||||
Title: chapter.Title,
|
||||
Content: util.NullStringToString(chapter.Content),
|
||||
Sort: int(chapter.Sort),
|
||||
Children: []types.CourseContent{}, // 初始化子章节切片
|
||||
}
|
||||
|
||||
// 查询当前章节的子章节(parentId=章节ID)
|
||||
childModels, err := l.model.FindByCourseAndParent(l.ctx, int64(courseId), chapter.Id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("查询子章节失败(章节ID:%d):%w", chapter.Id, err)
|
||||
}
|
||||
|
||||
// 转换子章节为响应格式,添加到章节的Children字段
|
||||
for _, child := range childModels {
|
||||
respChild := types.CourseContent{
|
||||
Id: int(child.Id),
|
||||
CourseId: int(child.CourseId),
|
||||
ParentId: int(child.ParentId),
|
||||
Title: child.Title,
|
||||
Content: NullStringToString(child.Content),
|
||||
Sort: int(child.Sort),
|
||||
Children: []types.CourseContent{}, // 子章节暂无下级,默认空切片
|
||||
}
|
||||
respChapter.Children = append(respChapter.Children, respChild)
|
||||
}
|
||||
|
||||
// 添加章节到树形列表
|
||||
treeList = append(treeList, respChapter)
|
||||
}
|
||||
|
||||
return treeList, nil
|
||||
}
|
||||
|
||||
// queryFlatContent 查询平级内容(兼容原有逻辑:按courseId和parentId查指定层级)
|
||||
func (l *GetContentListLogic) queryFlatContent(courseId, parentId int) ([]types.CourseContent, error) {
|
||||
// 调用数据层查询平级内容
|
||||
contentModels, err := l.model.FindByCourseAndParent(l.ctx, int64(courseId), int64(parentId))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 转换为响应格式
|
||||
var flatList []types.CourseContent
|
||||
for _, modelItem := range contentModels {
|
||||
flatList = append(flatList, types.CourseContent{
|
||||
Id: int(modelItem.Id),
|
||||
CourseId: int(modelItem.CourseId),
|
||||
ParentId: int(modelItem.ParentId),
|
||||
Title: modelItem.Title,
|
||||
Content: NullStringToString(modelItem.Content),
|
||||
Sort: int(modelItem.Sort),
|
||||
Children: []types.CourseContent{}, // 平级查询时子章节为空
|
||||
})
|
||||
}
|
||||
|
||||
return flatList, nil
|
||||
}
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
package types
|
||||
|
||||
type AddContentReq struct {
|
||||
CourseId int `json:"course_id" form:"course_id"` // 课程ID,必填
|
||||
ParentId int `json:"parent_id" form:"parent_id"` // 父级ID,必填
|
||||
Title string `json:"title" form:"title"` // 标题,必填
|
||||
Content string `json:"content" form:"content"` // 内容详情,可选
|
||||
Sort int `json:"sort" form:"sort"` // 排序,可选
|
||||
CourseId int `json:"course_id"` // 课程ID,必填
|
||||
ParentId int `json:"parent_id"` // 父级ID,必填
|
||||
Title string `json:"title"` // 标题,必填
|
||||
Content string `json:"content"` // 内容详情,可选
|
||||
Sort int `json:"sort"` // 排序,可选
|
||||
}
|
||||
|
||||
type AddContentResp struct {
|
||||
@@ -23,12 +23,13 @@ type BaseResp struct {
|
||||
}
|
||||
|
||||
type CourseContent struct {
|
||||
Id int `json:"id"` // 内容ID(主键)
|
||||
CourseId int `json:"course_id"` // 关联课程ID
|
||||
ParentId int `json:"parent_id"` // 父级ID(0表示章节,>0表示小节)
|
||||
Title string `json:"title"` // 章节/小节标题
|
||||
Content string `json:"content"` // 内容详情
|
||||
Sort int `json:"sort"` // 排序
|
||||
Id int `json:"id"` // 内容ID(主键)
|
||||
CourseId int `json:"course_id"` // 关联课程ID
|
||||
ParentId int `json:"parent_id"` // 父级ID(0表示章节,>0表示小节)
|
||||
Title string `json:"title"` // 章节/小节标题
|
||||
Content string `json:"content"` // 内容详情
|
||||
Sort int `json:"sort"` // 排序
|
||||
Children []CourseContent `json:"children"` // 子章节列表(仅章节有值)
|
||||
}
|
||||
|
||||
type DeleteContentReq struct {
|
||||
@@ -40,8 +41,8 @@ type DeleteContentResp struct {
|
||||
}
|
||||
|
||||
type GetContentListReq struct {
|
||||
CourseId int `json:"course_id" form:"course_id"` // 课程ID,必填
|
||||
ParentId int `json:"parent_id" form:"parent_id"` // 父级ID,可选,0表示获取章节
|
||||
CourseId int `json:"course_id"` // 课程ID,必填
|
||||
ParentId int `json:"parent_id"` // 父级ID,可选,0表示获取章节
|
||||
}
|
||||
|
||||
type GetContentListResp struct {
|
||||
@@ -59,10 +60,10 @@ type GetContentResp struct {
|
||||
}
|
||||
|
||||
type UpdateContentReq struct {
|
||||
Id int `json:"id" form:"id"` // 内容ID,必填
|
||||
Title string `json:"title" form:"title"` // 标题,可选
|
||||
Content string `json:"content" form:"content"` // 内容详情,可选
|
||||
Sort int `json:"sort" form:"sort"` // 排序,可选
|
||||
Id int `json:"id" ` // 内容ID,必填
|
||||
Title string `json:"title" ` // 标题,可选
|
||||
Content string `json:"content" ` // 内容详情,可选
|
||||
Sort int `json:"sort" ` // 排序,可选
|
||||
}
|
||||
|
||||
type UpdateContentResp struct {
|
||||
|
||||
@@ -6,6 +6,8 @@ package course_resource
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/JACKYMYPERSON/hldrCenter/config"
|
||||
"github.com/JACKYMYPERSON/hldrCenter/internal/course_resource/internal/logic/course_resource"
|
||||
@@ -18,10 +20,18 @@ import (
|
||||
func DeleteCourseResourceHandler(cfg *config.Config) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.DeleteCourseResourceReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
pathParts := strings.Split(r.URL.Path, "/")
|
||||
if len(pathParts) < 3 { // 确保路径格式正确
|
||||
httpx.ErrorCtx(r.Context(), w, fmt.Errorf("invalid path format"))
|
||||
return
|
||||
}
|
||||
idStr := pathParts[3]
|
||||
id, err := strconv.ParseInt(idStr, 10, 64)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, fmt.Errorf("invalid meeting ID"))
|
||||
return
|
||||
}
|
||||
req.Id = int(id)
|
||||
|
||||
mysqlCfg := cfg.MySQL
|
||||
dsn := fmt.Sprintf(
|
||||
|
||||
@@ -6,6 +6,8 @@ package course_resource
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/JACKYMYPERSON/hldrCenter/config"
|
||||
"github.com/JACKYMYPERSON/hldrCenter/internal/course_resource/internal/logic/course_resource"
|
||||
@@ -18,10 +20,19 @@ import (
|
||||
func GetCourseResourceHandler(cfg *config.Config) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.GetCourseResourceReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
pathParts := strings.Split(r.URL.Path, "/")
|
||||
fmt.Println(pathParts)
|
||||
if len(pathParts) < 3 { // 确保路径格式正确
|
||||
httpx.ErrorCtx(r.Context(), w, fmt.Errorf("invalid path format"))
|
||||
return
|
||||
}
|
||||
idStr := pathParts[3]
|
||||
id, err := strconv.ParseInt(idStr, 10, 64)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, fmt.Errorf("invalid meeting ID"))
|
||||
return
|
||||
}
|
||||
req.Id = int(id)
|
||||
|
||||
mysqlCfg := cfg.MySQL
|
||||
dsn := fmt.Sprintf(
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
package types
|
||||
|
||||
type CreateCourseResourceReq struct {
|
||||
CourseId int `json:"course_id" form:"course_id" validate:"required"`
|
||||
Title string `json:"title" form:"title" validate:"required,max=255"`
|
||||
ResourceUrl string `json:"resource_url" form:"resource_url" validate:"required,max=512"`
|
||||
Size int `json:"size" form:"size" validate:"omitempty,min=0"`
|
||||
Sort int `json:"sort" form:"sort" validate:"omitempty,min=0"`
|
||||
CourseId int `json:"course_id" validate:"required"`
|
||||
Title string `json:"title" validate:"required,max=255"`
|
||||
ResourceUrl string `json:"resource_url" validate:"required,max=512"`
|
||||
Size int `json:"size" validate:"omitempty,min=0"`
|
||||
Sort int `json:"sort" validate:"omitempty,min=0"`
|
||||
}
|
||||
|
||||
type CreateCourseResourceResp struct {
|
||||
@@ -38,9 +38,9 @@ type GetCourseResourceResp struct {
|
||||
}
|
||||
|
||||
type ListCourseResourceReq struct {
|
||||
CourseId int `form:"course_id" validate:"omitempty"`
|
||||
Page int `form:"page" validate:"required,min=1"`
|
||||
PageSize int `form:"page_size" validate:"required,min=1,max=100"`
|
||||
CourseId int `json:"course_id" validate:"omitempty"`
|
||||
Page int `json:"page" validate:"required,min=1"`
|
||||
PageSize int `json:"page_size" validate:"required,min=1,max=100"`
|
||||
}
|
||||
|
||||
type ListCourseResourceResp struct {
|
||||
|
||||
Reference in New Issue
Block a user