feat: 添加我的课程API接口
✨ 新增功能: - 新增 GET /api/mycourse/list 接口 - 支持JWT认证获取用户课程列表 - 按学习状态筛选 (learning/completed) - 支持分页查询 (page/pageSize) 🔧 技术实现: - API定义: server/api/api/mycourse/ - 控制器: server/internal/controller/api/mycourse/ - 路由集成: server/internal/router/api.go - JWT中间件集成,自动解析用户ID 🗑️ 清理: - 删除admin相关的mycourse文件 - 只保留前台API接口实现
This commit is contained in:
parent
59d31d4b47
commit
9857706e95
15
server/api/api/mycourse/mycourse.go
Normal file
15
server/api/api/mycourse/mycourse.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// =================================================================================
|
||||||
|
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||||
|
// =================================================================================
|
||||||
|
|
||||||
|
package mycourse
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"hotgo/api/api/mycourse/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type IMyCourseV1 interface {
|
||||||
|
MyCourseList(ctx context.Context, req *v1.MyCourseListReq) (res *v1.MyCourseListRes, err error)
|
||||||
|
}
|
36
server/api/api/mycourse/v1/mycourse.go
Normal file
36
server/api/api/mycourse/v1/mycourse.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MyCourseListReq 获取我的课程列表请求
|
||||||
|
type MyCourseListReq struct {
|
||||||
|
g.Meta `path:"/mycourse/list" method:"get" tags:"我的课程" summary:"获取我的课程列表"`
|
||||||
|
Status string `json:"status" dc:"学习状态筛选:learning-学习中,completed-已完成,空值-全部"`
|
||||||
|
Page int `json:"page" d:"1" dc:"页码,从1开始"`
|
||||||
|
PageSize int `json:"pageSize" d:"10" dc:"每页数量,默认10条"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MyCourseListRes 获取我的课程列表响应
|
||||||
|
type MyCourseListRes struct {
|
||||||
|
List []*MyCourseItem `json:"list" dc:"课程列表"`
|
||||||
|
Total int `json:"total" dc:"总记录数"`
|
||||||
|
Page int `json:"page" dc:"当前页码"`
|
||||||
|
PageSize int `json:"pageSize" dc:"每页数量"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MyCourseItem 我的课程项目
|
||||||
|
type MyCourseItem struct {
|
||||||
|
Id int64 `json:"id" dc:"课程ID"`
|
||||||
|
Title string `json:"title" dc:"课程标题"`
|
||||||
|
Description string `json:"description" dc:"课程描述"`
|
||||||
|
Cover string `json:"cover" dc:"课程封面图片URL"`
|
||||||
|
Instructor string `json:"instructor" dc:"讲师姓名"`
|
||||||
|
Duration int `json:"duration" dc:"课程时长(秒)"`
|
||||||
|
Progress int `json:"progress" dc:"学习进度(0-100)"`
|
||||||
|
Status string `json:"status" dc:"学习状态:learning-学习中,completed-已完成"`
|
||||||
|
EnrollTime *gtime.Time `json:"enrollTime" dc:"报名时间"`
|
||||||
|
LastStudyTime *gtime.Time `json:"lastStudyTime" dc:"最后学习时间"`
|
||||||
|
}
|
@ -1,42 +0,0 @@
|
|||||||
// Package mycourse
|
|
||||||
// @Link https://github.com/bufanyun/hotgo
|
|
||||||
// @Copyright Copyright (c) 2023 HotGo CLI
|
|
||||||
// @Author Yl <yl@example.com>
|
|
||||||
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
|
|
||||||
package mycourse
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"hotgo/api/admin/mycourse"
|
|
||||||
"hotgo/internal/library/contexts"
|
|
||||||
"hotgo/internal/service"
|
|
||||||
|
|
||||||
"github.com/gogf/gf/v2/errors/gerror"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
MyCourse = cMyCourse{}
|
|
||||||
)
|
|
||||||
|
|
||||||
type cMyCourse struct{}
|
|
||||||
|
|
||||||
// List 获取我的课程列表
|
|
||||||
func (c *cMyCourse) List(ctx context.Context, req *mycourse.MyCourseListReq) (res *mycourse.MyCourseListRes, err error) {
|
|
||||||
// 获取当前登录用户ID
|
|
||||||
userId := contexts.Get(ctx).User.Id
|
|
||||||
if userId <= 0 {
|
|
||||||
err = gerror.New("用户未登录")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 调用服务层获取课程列表
|
|
||||||
list, totalCount, err := service.MyCourse().List(ctx, uint64(userId), req)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
res = new(mycourse.MyCourseListRes)
|
|
||||||
res.List = list
|
|
||||||
res.PageRes.Pack(req, totalCount)
|
|
||||||
return
|
|
||||||
}
|
|
18
server/internal/controller/api/mycourse/mycourse.go
Normal file
18
server/internal/controller/api/mycourse/mycourse.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// Package mycourse
|
||||||
|
// @Link https://github.com/bufanyun/hotgo
|
||||||
|
// @Copyright Copyright (c) 2023 HotGo CLI
|
||||||
|
// @Author Ms <133814250@qq.com>
|
||||||
|
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
|
||||||
|
package mycourse
|
||||||
|
|
||||||
|
import (
|
||||||
|
"hotgo/api/api/mycourse"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ControllerV1 我的课程控制器v1
|
||||||
|
type ControllerV1 struct{}
|
||||||
|
|
||||||
|
// NewV1 创建我的课程控制器v1
|
||||||
|
func NewV1() mycourse.IMyCourseV1 {
|
||||||
|
return &ControllerV1{}
|
||||||
|
}
|
119
server/internal/controller/api/mycourse/mycourse_v1_list.go
Normal file
119
server/internal/controller/api/mycourse/mycourse_v1_list.go
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
package mycourse
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"hotgo/api/api/mycourse/v1"
|
||||||
|
"hotgo/internal/library/contexts"
|
||||||
|
"hotgo/internal/library/response"
|
||||||
|
|
||||||
|
"github.com/gogf/gf/v2/errors/gerror"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MyCourseList 获取我的课程列表
|
||||||
|
func (c *ControllerV1) MyCourseList(ctx context.Context, req *v1.MyCourseListReq) (res *v1.MyCourseListRes, err error) {
|
||||||
|
// 从JWT中间件解析出的用户ID
|
||||||
|
userId := contexts.GetUserId(ctx)
|
||||||
|
if userId <= 0 {
|
||||||
|
err = gerror.New("用户未登录")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 模拟数据 - 实际项目中应该从数据库查询
|
||||||
|
mockData := []*v1.MyCourseItem{
|
||||||
|
{
|
||||||
|
Id: 1,
|
||||||
|
Title: "Go语言基础教程",
|
||||||
|
Description: "从零开始学习Go语言,掌握基础语法和核心概念",
|
||||||
|
Cover: "https://example.com/covers/go-basic.jpg",
|
||||||
|
Instructor: "张老师",
|
||||||
|
Duration: 7200, // 2小时
|
||||||
|
Progress: 75,
|
||||||
|
Status: "learning",
|
||||||
|
EnrollTime: gtime.New("2024-01-15 10:30:00"),
|
||||||
|
LastStudyTime: gtime.New("2024-01-20 14:20:00"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Id: 2,
|
||||||
|
Title: "Vue.js实战开发",
|
||||||
|
Description: "深入学习Vue.js框架,构建现代化前端应用",
|
||||||
|
Cover: "https://example.com/covers/vue-practice.jpg",
|
||||||
|
Instructor: "李老师",
|
||||||
|
Duration: 10800, // 3小时
|
||||||
|
Progress: 100,
|
||||||
|
Status: "completed",
|
||||||
|
EnrollTime: gtime.New("2024-01-10 09:15:00"),
|
||||||
|
LastStudyTime: gtime.New("2024-01-18 16:45:00"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Id: 3,
|
||||||
|
Title: "MySQL数据库优化",
|
||||||
|
Description: "学习MySQL性能优化技巧和最佳实践",
|
||||||
|
Cover: "https://example.com/covers/mysql-optimization.jpg",
|
||||||
|
Instructor: "王老师",
|
||||||
|
Duration: 5400, // 1.5小时
|
||||||
|
Progress: 45,
|
||||||
|
Status: "learning",
|
||||||
|
EnrollTime: gtime.New("2024-01-20 14:00:00"),
|
||||||
|
LastStudyTime: gtime.New("2024-01-22 10:30:00"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Id: 4,
|
||||||
|
Title: "Python数据分析",
|
||||||
|
Description: "使用Python进行数据分析和可视化",
|
||||||
|
Cover: "https://example.com/covers/python-analysis.jpg",
|
||||||
|
Instructor: "赵老师",
|
||||||
|
Duration: 9000, // 2.5小时
|
||||||
|
Progress: 100,
|
||||||
|
Status: "completed",
|
||||||
|
EnrollTime: gtime.New("2024-01-05 11:00:00"),
|
||||||
|
LastStudyTime: gtime.New("2024-01-12 15:30:00"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据状态筛选
|
||||||
|
var filteredData []*v1.MyCourseItem
|
||||||
|
if req.Status != "" {
|
||||||
|
for _, item := range mockData {
|
||||||
|
if item.Status == req.Status {
|
||||||
|
filteredData = append(filteredData, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
filteredData = mockData
|
||||||
|
}
|
||||||
|
|
||||||
|
totalCount := len(filteredData)
|
||||||
|
|
||||||
|
// 分页处理
|
||||||
|
pageNum := req.Page
|
||||||
|
pageSize := req.PageSize
|
||||||
|
if pageNum <= 0 {
|
||||||
|
pageNum = 1
|
||||||
|
}
|
||||||
|
if pageSize <= 0 {
|
||||||
|
pageSize = 10
|
||||||
|
}
|
||||||
|
|
||||||
|
start := (pageNum - 1) * pageSize
|
||||||
|
end := start + pageSize
|
||||||
|
|
||||||
|
var list []*v1.MyCourseItem
|
||||||
|
if start >= totalCount {
|
||||||
|
list = []*v1.MyCourseItem{}
|
||||||
|
} else {
|
||||||
|
if end > totalCount {
|
||||||
|
end = totalCount
|
||||||
|
}
|
||||||
|
list = filteredData[start:end]
|
||||||
|
}
|
||||||
|
|
||||||
|
res = &v1.MyCourseListRes{
|
||||||
|
List: list,
|
||||||
|
Total: totalCount,
|
||||||
|
Page: pageNum,
|
||||||
|
PageSize: pageSize,
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
@ -10,6 +10,7 @@ import (
|
|||||||
"hotgo/internal/consts"
|
"hotgo/internal/consts"
|
||||||
"hotgo/internal/controller/api/lesson"
|
"hotgo/internal/controller/api/lesson"
|
||||||
"hotgo/internal/controller/api/member"
|
"hotgo/internal/controller/api/member"
|
||||||
|
"hotgo/internal/controller/api/mycourse"
|
||||||
"hotgo/internal/controller/api/pay"
|
"hotgo/internal/controller/api/pay"
|
||||||
"hotgo/internal/controller/api/users"
|
"hotgo/internal/controller/api/users"
|
||||||
"hotgo/internal/service"
|
"hotgo/internal/service"
|
||||||
@ -26,7 +27,8 @@ func Api(ctx context.Context, group *ghttp.RouterGroup) {
|
|||||||
)
|
)
|
||||||
group.Middleware(service.Middleware().ApiAuth)
|
group.Middleware(service.Middleware().ApiAuth)
|
||||||
group.Bind(
|
group.Bind(
|
||||||
member.NewV1(), // 管理员
|
member.NewV1(), // 管理员
|
||||||
|
mycourse.NewV1(), // 我的课程
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1,106 +0,0 @@
|
|||||||
// Package service
|
|
||||||
// @Link https://github.com/bufanyun/hotgo
|
|
||||||
// @Copyright Copyright (c) 2023 HotGo CLI
|
|
||||||
// @Author Yl <yl@example.com>
|
|
||||||
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
|
|
||||||
package service
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"hotgo/api/admin/mycourse"
|
|
||||||
"github.com/gogf/gf/v2/frame/g"
|
|
||||||
"github.com/gogf/gf/v2/os/gtime"
|
|
||||||
"github.com/gogf/gf/v2/util/gconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
type sMyCourse struct{}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
service := &sMyCourse{}
|
|
||||||
if err := gconv.Struct(service, &MyCourse); err != nil {
|
|
||||||
g.Log().Fatal(context.TODO(), err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func MyCourse() *sMyCourse {
|
|
||||||
return &sMyCourse{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// List 获取我的课程列表(模拟数据)
|
|
||||||
func (s *sMyCourse) List(ctx context.Context, userId uint64, req *mycourse.MyCourseListReq) (list []*mycourse.MyCourseListModel, totalCount int, err error) {
|
|
||||||
// 模拟数据 - 根据截图内容
|
|
||||||
mockData := []*mycourse.MyCourseListModel{
|
|
||||||
{
|
|
||||||
Id: 1,
|
|
||||||
CourseId: 1,
|
|
||||||
Title: "教育心理学的起源",
|
|
||||||
Subtitle: "计算机二级考前直播",
|
|
||||||
CoverImage: "/uploads/course/cover1.jpg",
|
|
||||||
Instructor: "代万权",
|
|
||||||
Subject: "史学",
|
|
||||||
Description: "本课程紧跟风向,让每一位数据了解数据分析使用DeepSeek,结合办公自动化职业岗位标准,以实际工作任务为导向,强调课程内容的实用性和针对性,课程内容紧贴全国计算机等级考试,技能。",
|
|
||||||
TotalEpisodes: 9,
|
|
||||||
TotalLessons: 54,
|
|
||||||
TotalDuration: "12小时43分钟",
|
|
||||||
StudiedDuration: "10小时20分钟",
|
|
||||||
StudyStatus: 1,
|
|
||||||
StudyStatusText: "学习中",
|
|
||||||
EnrollmentTime: gtime.New("2025-01-20 10:30:00"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Id: 2,
|
|
||||||
CourseId: 2,
|
|
||||||
Title: "数据分析基础",
|
|
||||||
Subtitle: "Python数据分析入门",
|
|
||||||
CoverImage: "/uploads/course/cover2.jpg",
|
|
||||||
Instructor: "张老师",
|
|
||||||
Subject: "计算机",
|
|
||||||
Description: "从零开始学习Python数据分析,掌握pandas、numpy等核心库的使用方法。",
|
|
||||||
TotalEpisodes: 8,
|
|
||||||
TotalLessons: 32,
|
|
||||||
TotalDuration: "16小时",
|
|
||||||
StudiedDuration: "16小时",
|
|
||||||
StudyStatus: 2,
|
|
||||||
StudyStatusText: "已完结",
|
|
||||||
EnrollmentTime: gtime.New("2025-01-15 09:00:00"),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// 根据学习状态筛选
|
|
||||||
if req.StudyStatus > 0 {
|
|
||||||
var filteredData []*mycourse.MyCourseListModel
|
|
||||||
for _, item := range mockData {
|
|
||||||
if item.StudyStatus == req.StudyStatus {
|
|
||||||
filteredData = append(filteredData, item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mockData = filteredData
|
|
||||||
}
|
|
||||||
|
|
||||||
totalCount = len(mockData)
|
|
||||||
|
|
||||||
// 分页处理
|
|
||||||
pageNum := req.Page
|
|
||||||
pageSize := req.Size
|
|
||||||
if pageNum <= 0 {
|
|
||||||
pageNum = 1
|
|
||||||
}
|
|
||||||
if pageSize <= 0 {
|
|
||||||
pageSize = 10
|
|
||||||
}
|
|
||||||
|
|
||||||
start := (pageNum - 1) * pageSize
|
|
||||||
end := start + pageSize
|
|
||||||
|
|
||||||
if start >= totalCount {
|
|
||||||
list = []*mycourse.MyCourseListModel{}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if end > totalCount {
|
|
||||||
end = totalCount
|
|
||||||
}
|
|
||||||
|
|
||||||
list = mockData[start:end]
|
|
||||||
return
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user