完成课程资源

This commit is contained in:
2025-11-02 12:31:27 +08:00
parent 7f8c25484e
commit 17ecdad42f
8 changed files with 552 additions and 259 deletions

View File

@@ -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",

View File

@@ -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(

View File

@@ -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 层无此方法,可构造 SQLSELECT * 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
}

View File

@@ -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"` // 父级ID0表示章节>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"` // 父级ID0表示章节>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 {

View File

@@ -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(

View File

@@ -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(

View File

@@ -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 {