Compare commits
No commits in common. "05bd8e6eff32b09dd0ae66838bc1b5252d3c7342" and "9857706e9544066dc543182024f435d4e1ce8acc" have entirely different histories.
05bd8e6eff
...
9857706e95
13
Dockerfile
13
Dockerfile
@ -11,6 +11,8 @@ RUN npm install -g pnpm
|
|||||||
# 设置工作目录
|
# 设置工作目录
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 复制后端
|
||||||
|
COPY ./server ./server
|
||||||
# 复制前端
|
# 复制前端
|
||||||
COPY ./web ./web
|
COPY ./web ./web
|
||||||
|
|
||||||
@ -19,9 +21,6 @@ WORKDIR /app/web
|
|||||||
RUN echo "y" |pnpm install
|
RUN echo "y" |pnpm install
|
||||||
RUN pnpm run build
|
RUN pnpm run build
|
||||||
|
|
||||||
# 复制后端
|
|
||||||
COPY ./server ./server
|
|
||||||
|
|
||||||
# 构建后端项目
|
# 构建后端项目
|
||||||
WORKDIR /app/server
|
WORKDIR /app/server
|
||||||
|
|
||||||
@ -34,12 +33,10 @@ RUN cp -rf ../web/dist/* ./resource/public/admin/
|
|||||||
|
|
||||||
# 编译hotgo服务端
|
# 编译hotgo服务端
|
||||||
RUN go env -w GOPROXY=https://goproxy.cn,direct
|
RUN go env -w GOPROXY=https://goproxy.cn,direct
|
||||||
RUN go mod tidy
|
RUN go mod download
|
||||||
|
|
||||||
# 安装gf
|
# 安装gf
|
||||||
# 将deploy里的gf文件复制到当前目录
|
RUN wget -O gf "https://github.com/gogf/gf/releases/latest/download/gf_$(go env GOOS)_$(go env GOARCH)" && chmod +x gf && ./gf install -y && rm ./gf
|
||||||
COPY ./deploy/gf .
|
|
||||||
RUN chmod +x gf && ./gf install -y && rm ./gf
|
|
||||||
|
|
||||||
RUN gf build
|
RUN gf build
|
||||||
|
|
||||||
@ -47,7 +44,7 @@ RUN gf build
|
|||||||
FROM alpine:latest
|
FROM alpine:latest
|
||||||
|
|
||||||
# 安装 ca-certificates 用于 HTTPS 请求
|
# 安装 ca-certificates 用于 HTTPS 请求
|
||||||
RUN apk --no-cache add ca-certificates tzdata ffmpeg
|
RUN apk --no-cache add ca-certificates tzdata
|
||||||
|
|
||||||
# 设置时区
|
# 设置时区
|
||||||
ENV TZ=Asia/Shanghai
|
ENV TZ=Asia/Shanghai
|
||||||
|
@ -35,14 +35,14 @@ services:
|
|||||||
--character-set-server=utf8mb4
|
--character-set-server=utf8mb4
|
||||||
--collation-server=utf8mb4_unicode_ci
|
--collation-server=utf8mb4_unicode_ci
|
||||||
--tls_version="TLSv1.2,TLSv1.3"
|
--tls_version="TLSv1.2,TLSv1.3"
|
||||||
|
--init-file /data/application/init.sql
|
||||||
--binlog_expire_logs_seconds=604800
|
--binlog_expire_logs_seconds=604800
|
||||||
# --init-file /data/application/init.sql
|
|
||||||
# --default-authentication-plugin=mysql_native_password
|
# --default-authentication-plugin=mysql_native_password
|
||||||
ports:
|
ports:
|
||||||
- 3306:3306
|
- 3306:3306
|
||||||
volumes:
|
volumes:
|
||||||
- mysql_data:/var/lib/mysql
|
- mysql_data:/var/lib/mysql
|
||||||
# - ./deploy/init.sql:/data/application/init.sql
|
- ./deploy/init.sql:/data/application/init.sql
|
||||||
networks:
|
networks:
|
||||||
- gmanager
|
- gmanager
|
||||||
healthcheck:
|
healthcheck:
|
||||||
|
129
docs/apifox_config.md
Normal file
129
docs/apifox_config.md
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
# Apifox接口文档配置指南 - 我的课程模块
|
||||||
|
|
||||||
|
## 项目基本信息
|
||||||
|
- **项目名称**:在线学习平台-我的课程模块
|
||||||
|
- **基础URL**:http://localhost:8000/admin
|
||||||
|
- **认证方式**:Bearer Token (JWT)
|
||||||
|
|
||||||
|
## 接口说明
|
||||||
|
|
||||||
|
### 获取我的课程列表
|
||||||
|
- **接口名称**:获取我的课程列表
|
||||||
|
- **请求方法**:GET
|
||||||
|
- **接口路径**:/mycourse/list
|
||||||
|
- **接口描述**:获取当前用户的课程列表,支持分页和状态筛选
|
||||||
|
|
||||||
|
**请求参数(Query):**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"page": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "页码,默认1",
|
||||||
|
"example": 1
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "每页数量,默认10",
|
||||||
|
"example": 10
|
||||||
|
},
|
||||||
|
"study_status": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "学习状态:0全部课程 1学习中 2已完结",
|
||||||
|
"example": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**响应示例:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 0,
|
||||||
|
"message": "success",
|
||||||
|
"data": {
|
||||||
|
"list": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"course_id": 1,
|
||||||
|
"title": "教育心理学的起源",
|
||||||
|
"subtitle": "计算机二级考前直播",
|
||||||
|
"cover_image": "/uploads/course/cover1.jpg",
|
||||||
|
"instructor": "代万权",
|
||||||
|
"subject": "史学",
|
||||||
|
"description": "本课程紧跟风向,让每一位数据了解数据分析使用DeepSeek,结合办公自动化职业岗位标准,以实际工作任务为导向,强调课程内容的实用性和针对性,课程内容紧贴全国计算机等级考试,技能。",
|
||||||
|
"total_episodes": 9,
|
||||||
|
"total_lessons": 54,
|
||||||
|
"total_duration": "12小时43分钟",
|
||||||
|
"studied_duration": "10小时20分钟",
|
||||||
|
"study_status": 1,
|
||||||
|
"study_status_text": "学习中",
|
||||||
|
"enrollment_time": "2025-01-20 10:30:00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"page": 1,
|
||||||
|
"size": 10,
|
||||||
|
"total": 2
|
||||||
|
},
|
||||||
|
"timestamp": 1706428800
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 认证配置
|
||||||
|
|
||||||
|
### JWT Token配置
|
||||||
|
1. 在Apifox项目设置中添加认证方式
|
||||||
|
2. 选择"Bearer Token"
|
||||||
|
3. 在请求头中添加:
|
||||||
|
```
|
||||||
|
Authorization: Bearer {token}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 环境变量配置
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"base_url": "http://localhost:8000",
|
||||||
|
"admin_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||||||
|
"user_id": 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 测试步骤
|
||||||
|
|
||||||
|
### 1. 启动服务
|
||||||
|
```bash
|
||||||
|
cd e:\桌面\Remote/OL-LearnPlatform-Admin-main\ol-learnplatform-admin/server
|
||||||
|
go run main.go
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 获取Token
|
||||||
|
先调用登录接口获取JWT Token:
|
||||||
|
- 方法:POST
|
||||||
|
- 路径:/admin/site/accountLogin
|
||||||
|
- 请求体:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"username": "admin",
|
||||||
|
"password": "123456",
|
||||||
|
"cid": "",
|
||||||
|
"code": ""
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 测试课程列表接口
|
||||||
|
- URL:http://localhost:8000/admin/mycourse/list
|
||||||
|
- 方法:GET
|
||||||
|
- 请求头:Authorization: Bearer {token}
|
||||||
|
- 参数:
|
||||||
|
- page: 1
|
||||||
|
- size: 10
|
||||||
|
- study_status: 1 (学习中) 或 2 (已完结) 或 0 (全部)
|
||||||
|
|
||||||
|
## 预期响应
|
||||||
|
|
||||||
|
接口将返回符合截图显示的课程数据:
|
||||||
|
- 课程标题和副标题
|
||||||
|
- 讲师和学科信息
|
||||||
|
- 课程描述
|
||||||
|
- 集数、节数、时长信息
|
||||||
|
- 学习状态(学习中/已完结)
|
||||||
|
|
||||||
|
这个简化版本只包含截图中实际需要的功能,没有添加额外的复杂功能。
|
@ -6,9 +6,8 @@
|
|||||||
package common
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"hotgo/internal/model/input/sysin"
|
|
||||||
|
|
||||||
"github.com/gogf/gf/v2/frame/g"
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"hotgo/internal/model/input/sysin"
|
||||||
)
|
)
|
||||||
|
|
||||||
// UploadFileReq 上传文件
|
// UploadFileReq 上传文件
|
||||||
@ -18,14 +17,6 @@ type UploadFileReq struct {
|
|||||||
|
|
||||||
type UploadFileRes *sysin.AttachmentListModel
|
type UploadFileRes *sysin.AttachmentListModel
|
||||||
|
|
||||||
type UploadVideoReq struct {
|
|
||||||
g.Meta `path:"/upload/video" tags:"附件" method:"post" summary:"上传视频"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type UploadVideoRes struct {
|
|
||||||
Path string `json:"path"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CheckMultipartReq 检查文件分片
|
// CheckMultipartReq 检查文件分片
|
||||||
type CheckMultipartReq struct {
|
type CheckMultipartReq struct {
|
||||||
g.Meta `path:"/upload/checkMultipart" tags:"附件" method:"post" summary:"检查文件分片"`
|
g.Meta `path:"/upload/checkMultipart" tags:"附件" method:"post" summary:"检查文件分片"`
|
||||||
|
43
server/api/admin/mycourse/mycourse.go
Normal file
43
server/api/admin/mycourse/mycourse.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// 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 (
|
||||||
|
"hotgo/internal/model/input/form"
|
||||||
|
|
||||||
|
"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:"获取我的课程列表"`
|
||||||
|
form.PageReq
|
||||||
|
StudyStatus int `json:"study_status" dc:"学习状态:0全部课程 1学习中 2已完结"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MyCourseListRes struct {
|
||||||
|
List []*MyCourseListModel `json:"list" dc:"课程列表"`
|
||||||
|
form.PageRes
|
||||||
|
}
|
||||||
|
|
||||||
|
type MyCourseListModel struct {
|
||||||
|
Id uint64 `json:"id" dc:"用户课程关联ID"`
|
||||||
|
CourseId uint64 `json:"course_id" dc:"课程ID"`
|
||||||
|
Title string `json:"title" dc:"课程标题"`
|
||||||
|
Subtitle string `json:"subtitle" dc:"课程副标题"`
|
||||||
|
CoverImage string `json:"cover_image" dc:"课程封面"`
|
||||||
|
Instructor string `json:"instructor" dc:"讲师"`
|
||||||
|
Subject string `json:"subject" dc:"学科"`
|
||||||
|
Description string `json:"description" dc:"课程描述"`
|
||||||
|
TotalEpisodes int `json:"total_episodes" dc:"总集数"`
|
||||||
|
TotalLessons int `json:"total_lessons" dc:"总节数"`
|
||||||
|
TotalDuration string `json:"total_duration" dc:"总时长"`
|
||||||
|
StudiedDuration string `json:"studied_duration" dc:"已学时长"`
|
||||||
|
StudyStatus int `json:"study_status" dc:"学习状态:1学习中 2已完结"`
|
||||||
|
StudyStatusText string `json:"study_status_text" dc:"学习状态文本"`
|
||||||
|
EnrollmentTime *gtime.Time `json:"enrollment_time" dc:"报名时间"`
|
||||||
|
}
|
@ -8,10 +8,7 @@ import (
|
|||||||
|
|
||||||
// 获取课程列表请求
|
// 获取课程列表请求
|
||||||
type LessonListReq struct {
|
type LessonListReq struct {
|
||||||
g.Meta `path:"/lesson/list" method:"get" tags:"课程" summary:"获取课程列表"`
|
g.Meta `path:"/lesson/list" method:"get" tags:"课程" summary:"获取课程列表"`
|
||||||
CategoryId int64 `json:"categoryId" dc:"分类ID"`
|
|
||||||
Difficuty int `json:"difficuty" dc:"难度"`
|
|
||||||
Subject string `json:"subject" dc:"专题"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取课程列表响应
|
// 获取课程列表响应
|
||||||
|
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:"最后学习时间"`
|
||||||
|
}
|
@ -7,17 +7,12 @@ package common
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"github.com/gogf/gf/v2/errors/gerror"
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
"hotgo/api/admin/common"
|
"hotgo/api/admin/common"
|
||||||
"hotgo/internal/library/storager"
|
"hotgo/internal/library/storager"
|
||||||
"hotgo/internal/service"
|
"hotgo/internal/service"
|
||||||
"hotgo/utility/validate"
|
"hotgo/utility/validate"
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/gogf/gf/v2/errors/gerror"
|
|
||||||
"github.com/gogf/gf/v2/frame/g"
|
|
||||||
"github.com/google/uuid"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var Upload = new(cUpload)
|
var Upload = new(cUpload)
|
||||||
@ -41,73 +36,6 @@ func (c *cUpload) UploadFile(ctx context.Context, _ *common.UploadFileReq) (res
|
|||||||
return service.CommonUpload().UploadFile(ctx, uploadType, file)
|
return service.CommonUpload().UploadFile(ctx, uploadType, file)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UploadVideo 上传视频
|
|
||||||
func (c *cUpload) UploadVideo(ctx context.Context, _ *common.UploadVideoReq) (res common.UploadVideoRes, err error) {
|
|
||||||
r := g.RequestFromCtx(ctx)
|
|
||||||
uploadType := r.Header.Get("uploadType")
|
|
||||||
if uploadType != "default" && !validate.InSlice(storager.KindSlice, uploadType) {
|
|
||||||
err = gerror.New("上传类型是无效的")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
file := r.GetUploadFile("file")
|
|
||||||
if file == nil {
|
|
||||||
err = gerror.New("没有找到上传的文件")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
m3u8UUID := uuid.New().String()
|
|
||||||
|
|
||||||
// 1. 保存上传视频到本地临时目录
|
|
||||||
tmpDir := "./tmp/video"
|
|
||||||
_, _ = file.Save(tmpDir + "/" + m3u8UUID)
|
|
||||||
tmpFile := tmpDir + "/" + m3u8UUID + "/" + file.Filename
|
|
||||||
|
|
||||||
// 2. 用ffmpeg切片为m3u8和ts文件
|
|
||||||
|
|
||||||
hlsDir := "./tmp/hls/" + m3u8UUID
|
|
||||||
_ = os.MkdirAll(hlsDir, 0755)
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
os.RemoveAll(hlsDir)
|
|
||||||
os.RemoveAll(tmpDir + "/" + m3u8UUID)
|
|
||||||
}()
|
|
||||||
|
|
||||||
m3u8File := m3u8UUID + ".m3u8"
|
|
||||||
m3u8Path := hlsDir + "/" + m3u8File
|
|
||||||
ffmpegCmd := exec.Command("ffmpeg", "-i", tmpFile, "-c:v", "libx264", "-hls_time", "10", "-hls_playlist_type", "vod", m3u8Path)
|
|
||||||
err = ffmpegCmd.Run()
|
|
||||||
if err != nil {
|
|
||||||
err = gerror.Wrap(err, "ffmpeg切片失败")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. 遍历切片目录,上传所有m3u8和ts文件到minio
|
|
||||||
minioDrive := new(storager.MinioDrive)
|
|
||||||
var m3u8MinioPath string
|
|
||||||
files, _ := filepath.Glob(hlsDir + "/*")
|
|
||||||
for _, f := range files {
|
|
||||||
fileInfo, errStat := os.Stat(f)
|
|
||||||
if errStat == nil && !fileInfo.IsDir() {
|
|
||||||
minioPath, err2 := minioDrive.UploadFromPath(ctx, f, filepath.Base(f))
|
|
||||||
if err2 != nil {
|
|
||||||
err = err2
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if filepath.Ext(f) == ".m3u8" {
|
|
||||||
m3u8MinioPath = minioPath
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m3u8MinioPath = storager.LastUrl(ctx, m3u8MinioPath, "minio")
|
|
||||||
|
|
||||||
// 4. 返回m3u8文件的minio路径
|
|
||||||
return common.UploadVideoRes{
|
|
||||||
Path: m3u8MinioPath,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CheckMultipart 检查文件分片
|
// CheckMultipart 检查文件分片
|
||||||
func (c *cUpload) CheckMultipart(ctx context.Context, req *common.CheckMultipartReq) (res *common.CheckMultipartRes, err error) {
|
func (c *cUpload) CheckMultipart(ctx context.Context, req *common.CheckMultipartReq) (res *common.CheckMultipartRes, err error) {
|
||||||
data, err := service.CommonUpload().CheckMultipart(ctx, &req.CheckMultipartInp)
|
data, err := service.CommonUpload().CheckMultipart(ctx, &req.CheckMultipartInp)
|
||||||
|
@ -9,42 +9,20 @@ import (
|
|||||||
|
|
||||||
"github.com/gogf/gf/v2/errors/gcode"
|
"github.com/gogf/gf/v2/errors/gcode"
|
||||||
"github.com/gogf/gf/v2/errors/gerror"
|
"github.com/gogf/gf/v2/errors/gerror"
|
||||||
"github.com/gogf/gf/v2/frame/g"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *ControllerV1) LessonList(ctx context.Context, req *v1.LessonListReq) (res *v1.LessonListRes, err error) {
|
func (c *ControllerV1) LessonList(ctx context.Context, req *v1.LessonListReq) (res *v1.LessonListRes, err error) {
|
||||||
// 构建查询条件
|
|
||||||
query := dao.Lesson.Ctx(ctx)
|
|
||||||
|
|
||||||
// 分类过滤
|
|
||||||
if req.CategoryId != -1 && req.CategoryId > 0 {
|
|
||||||
query = query.Where("category_id = ?", req.CategoryId)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 难度过滤
|
|
||||||
if req.Difficuty != -1 {
|
|
||||||
query = query.Where("difficulty = ?", req.Difficuty)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 专题过滤(subject为all时不过滤,否则用JSON_CONTAINS)
|
|
||||||
if req.Subject != "" && req.Subject != "all" {
|
|
||||||
query = query.WhereLike("subject", "%"+req.Subject+"%")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 查询课程列表
|
// 查询课程列表
|
||||||
var list []*entity.Lesson
|
var list []*entity.Lesson
|
||||||
err = query.Scan(&list)
|
err = dao.Lesson.Ctx(ctx).Scan(&list)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
g.Log().Error(ctx, err)
|
|
||||||
return nil, gerror.NewCode(gcode.CodeDbOperationError, "数据库错误")
|
return nil, gerror.NewCode(gcode.CodeDbOperationError, "数据库错误")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 统计总数
|
// 统计总数
|
||||||
total, err := query.Count()
|
total, err := dao.Lesson.Ctx(ctx).Count()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, gerror.NewCode(gcode.CodeDbOperationError, "统计总数失败")
|
return nil, gerror.NewCode(gcode.CodeDbOperationError, "统计总数失败")
|
||||||
}
|
}
|
||||||
|
|
||||||
res = &v1.LessonListRes{
|
res = &v1.LessonListRes{
|
||||||
List: list,
|
List: list,
|
||||||
Total: total,
|
Total: total,
|
||||||
|
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
|
||||||
|
}
|
0
server/internal/dao/activity.go
Executable file → Normal file
0
server/internal/dao/activity.go
Executable file → Normal file
0
server/internal/dao/activity_category.go
Executable file → Normal file
0
server/internal/dao/activity_category.go
Executable file → Normal file
0
server/internal/dao/addon_hgexample_table.go
Executable file → Normal file
0
server/internal/dao/addon_hgexample_table.go
Executable file → Normal file
0
server/internal/dao/addon_hgexample_tenant_order.go
Executable file → Normal file
0
server/internal/dao/addon_hgexample_tenant_order.go
Executable file → Normal file
0
server/internal/dao/admin_cash.go
Executable file → Normal file
0
server/internal/dao/admin_cash.go
Executable file → Normal file
0
server/internal/dao/admin_credits_log.go
Executable file → Normal file
0
server/internal/dao/admin_credits_log.go
Executable file → Normal file
0
server/internal/dao/admin_dept.go
Executable file → Normal file
0
server/internal/dao/admin_dept.go
Executable file → Normal file
0
server/internal/dao/admin_member.go
Executable file → Normal file
0
server/internal/dao/admin_member.go
Executable file → Normal file
0
server/internal/dao/admin_member_post.go
Executable file → Normal file
0
server/internal/dao/admin_member_post.go
Executable file → Normal file
0
server/internal/dao/admin_member_role.go
Executable file → Normal file
0
server/internal/dao/admin_member_role.go
Executable file → Normal file
0
server/internal/dao/admin_menu.go
Executable file → Normal file
0
server/internal/dao/admin_menu.go
Executable file → Normal file
0
server/internal/dao/admin_notice.go
Executable file → Normal file
0
server/internal/dao/admin_notice.go
Executable file → Normal file
0
server/internal/dao/admin_notice_read.go
Executable file → Normal file
0
server/internal/dao/admin_notice_read.go
Executable file → Normal file
0
server/internal/dao/admin_oauth.go
Executable file → Normal file
0
server/internal/dao/admin_oauth.go
Executable file → Normal file
0
server/internal/dao/admin_order.go
Executable file → Normal file
0
server/internal/dao/admin_order.go
Executable file → Normal file
0
server/internal/dao/admin_post.go
Executable file → Normal file
0
server/internal/dao/admin_post.go
Executable file → Normal file
0
server/internal/dao/admin_role.go
Executable file → Normal file
0
server/internal/dao/admin_role.go
Executable file → Normal file
0
server/internal/dao/admin_role_casbin.go
Executable file → Normal file
0
server/internal/dao/admin_role_casbin.go
Executable file → Normal file
0
server/internal/dao/admin_role_menu.go
Executable file → Normal file
0
server/internal/dao/admin_role_menu.go
Executable file → Normal file
0
server/internal/dao/internal/activity.go
Executable file → Normal file
0
server/internal/dao/internal/activity.go
Executable file → Normal file
0
server/internal/dao/internal/activity_category.go
Executable file → Normal file
0
server/internal/dao/internal/activity_category.go
Executable file → Normal file
0
server/internal/dao/internal/addon_hgexample_table.go
Executable file → Normal file
0
server/internal/dao/internal/addon_hgexample_table.go
Executable file → Normal file
0
server/internal/dao/internal/addon_hgexample_tenant_order.go
Executable file → Normal file
0
server/internal/dao/internal/addon_hgexample_tenant_order.go
Executable file → Normal file
0
server/internal/dao/internal/admin_cash.go
Executable file → Normal file
0
server/internal/dao/internal/admin_cash.go
Executable file → Normal file
0
server/internal/dao/internal/admin_credits_log.go
Executable file → Normal file
0
server/internal/dao/internal/admin_credits_log.go
Executable file → Normal file
0
server/internal/dao/internal/admin_dept.go
Executable file → Normal file
0
server/internal/dao/internal/admin_dept.go
Executable file → Normal file
0
server/internal/dao/internal/admin_member.go
Executable file → Normal file
0
server/internal/dao/internal/admin_member.go
Executable file → Normal file
0
server/internal/dao/internal/admin_member_post.go
Executable file → Normal file
0
server/internal/dao/internal/admin_member_post.go
Executable file → Normal file
0
server/internal/dao/internal/admin_member_role.go
Executable file → Normal file
0
server/internal/dao/internal/admin_member_role.go
Executable file → Normal file
0
server/internal/dao/internal/admin_menu.go
Executable file → Normal file
0
server/internal/dao/internal/admin_menu.go
Executable file → Normal file
0
server/internal/dao/internal/admin_notice.go
Executable file → Normal file
0
server/internal/dao/internal/admin_notice.go
Executable file → Normal file
0
server/internal/dao/internal/admin_notice_read.go
Executable file → Normal file
0
server/internal/dao/internal/admin_notice_read.go
Executable file → Normal file
0
server/internal/dao/internal/admin_oauth.go
Executable file → Normal file
0
server/internal/dao/internal/admin_oauth.go
Executable file → Normal file
0
server/internal/dao/internal/admin_order.go
Executable file → Normal file
0
server/internal/dao/internal/admin_order.go
Executable file → Normal file
0
server/internal/dao/internal/admin_post.go
Executable file → Normal file
0
server/internal/dao/internal/admin_post.go
Executable file → Normal file
0
server/internal/dao/internal/admin_role.go
Executable file → Normal file
0
server/internal/dao/internal/admin_role.go
Executable file → Normal file
0
server/internal/dao/internal/admin_role_casbin.go
Executable file → Normal file
0
server/internal/dao/internal/admin_role_casbin.go
Executable file → Normal file
0
server/internal/dao/internal/admin_role_menu.go
Executable file → Normal file
0
server/internal/dao/internal/admin_role_menu.go
Executable file → Normal file
4
server/internal/dao/internal/lesson.go
Executable file → Normal file
4
server/internal/dao/internal/lesson.go
Executable file → Normal file
@ -41,8 +41,6 @@ type LessonColumns struct {
|
|||||||
CreatedTime string // 创建时间
|
CreatedTime string // 创建时间
|
||||||
UpdatedBy string // 更新人
|
UpdatedBy string // 更新人
|
||||||
UpdatedTime string // 更新时间
|
UpdatedTime string // 更新时间
|
||||||
Difficulty string // 课程难度
|
|
||||||
Subject string // 所属专题
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// lessonColumns holds the columns for the table hg_lesson.
|
// lessonColumns holds the columns for the table hg_lesson.
|
||||||
@ -67,8 +65,6 @@ var lessonColumns = LessonColumns{
|
|||||||
CreatedTime: "created_time",
|
CreatedTime: "created_time",
|
||||||
UpdatedBy: "updated_by",
|
UpdatedBy: "updated_by",
|
||||||
UpdatedTime: "updated_time",
|
UpdatedTime: "updated_time",
|
||||||
Difficulty: "difficulty",
|
|
||||||
Subject: "subject",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLessonDao creates and returns a new DAO object for table data access.
|
// NewLessonDao creates and returns a new DAO object for table data access.
|
||||||
|
0
server/internal/dao/internal/lesson_category.go
Executable file → Normal file
0
server/internal/dao/internal/lesson_category.go
Executable file → Normal file
2
server/internal/dao/internal/lesson_section.go
Executable file → Normal file
2
server/internal/dao/internal/lesson_section.go
Executable file → Normal file
@ -27,7 +27,7 @@ type LessonSectionColumns struct {
|
|||||||
Name string // 章节名
|
Name string // 章节名
|
||||||
SortOrder string // 排序号
|
SortOrder string // 排序号
|
||||||
ParentId string // 父章节id
|
ParentId string // 父章节id
|
||||||
Level string // 章节层级
|
Level string // 章节层级;1=章,2=节
|
||||||
Revision string // 乐观锁
|
Revision string // 乐观锁
|
||||||
CreatedBy string // 创建人
|
CreatedBy string // 创建人
|
||||||
CreatedTime string // 创建时间
|
CreatedTime string // 创建时间
|
||||||
|
0
server/internal/dao/internal/pay_log.go
Executable file → Normal file
0
server/internal/dao/internal/pay_log.go
Executable file → Normal file
0
server/internal/dao/internal/pay_refund.go
Executable file → Normal file
0
server/internal/dao/internal/pay_refund.go
Executable file → Normal file
0
server/internal/dao/internal/sys_addons_config.go
Executable file → Normal file
0
server/internal/dao/internal/sys_addons_config.go
Executable file → Normal file
0
server/internal/dao/internal/sys_attachment.go
Executable file → Normal file
0
server/internal/dao/internal/sys_attachment.go
Executable file → Normal file
0
server/internal/dao/internal/sys_blacklist.go
Executable file → Normal file
0
server/internal/dao/internal/sys_blacklist.go
Executable file → Normal file
0
server/internal/dao/internal/sys_config.go
Executable file → Normal file
0
server/internal/dao/internal/sys_config.go
Executable file → Normal file
0
server/internal/dao/internal/sys_cron.go
Executable file → Normal file
0
server/internal/dao/internal/sys_cron.go
Executable file → Normal file
0
server/internal/dao/internal/sys_cron_group.go
Executable file → Normal file
0
server/internal/dao/internal/sys_cron_group.go
Executable file → Normal file
0
server/internal/dao/internal/sys_dict_data.go
Executable file → Normal file
0
server/internal/dao/internal/sys_dict_data.go
Executable file → Normal file
0
server/internal/dao/internal/sys_dict_type.go
Executable file → Normal file
0
server/internal/dao/internal/sys_dict_type.go
Executable file → Normal file
0
server/internal/dao/internal/sys_ems_log.go
Executable file → Normal file
0
server/internal/dao/internal/sys_ems_log.go
Executable file → Normal file
0
server/internal/dao/internal/sys_gen_codes.go
Executable file → Normal file
0
server/internal/dao/internal/sys_gen_codes.go
Executable file → Normal file
0
server/internal/dao/internal/sys_gen_curd_demo.go
Executable file → Normal file
0
server/internal/dao/internal/sys_gen_curd_demo.go
Executable file → Normal file
0
server/internal/dao/internal/sys_gen_tree_demo.go
Executable file → Normal file
0
server/internal/dao/internal/sys_gen_tree_demo.go
Executable file → Normal file
0
server/internal/dao/internal/sys_log.go
Executable file → Normal file
0
server/internal/dao/internal/sys_log.go
Executable file → Normal file
0
server/internal/dao/internal/sys_login_log.go
Executable file → Normal file
0
server/internal/dao/internal/sys_login_log.go
Executable file → Normal file
0
server/internal/dao/internal/sys_provinces.go
Executable file → Normal file
0
server/internal/dao/internal/sys_provinces.go
Executable file → Normal file
0
server/internal/dao/internal/sys_serve_license.go
Executable file → Normal file
0
server/internal/dao/internal/sys_serve_license.go
Executable file → Normal file
0
server/internal/dao/internal/sys_serve_log.go
Executable file → Normal file
0
server/internal/dao/internal/sys_serve_log.go
Executable file → Normal file
0
server/internal/dao/internal/sys_sms_log.go
Executable file → Normal file
0
server/internal/dao/internal/sys_sms_log.go
Executable file → Normal file
0
server/internal/dao/internal/test_category.go
Executable file → Normal file
0
server/internal/dao/internal/test_category.go
Executable file → Normal file
0
server/internal/dao/internal/users.go
Executable file → Normal file
0
server/internal/dao/internal/users.go
Executable file → Normal file
0
server/internal/dao/lesson.go
Executable file → Normal file
0
server/internal/dao/lesson.go
Executable file → Normal file
0
server/internal/dao/lesson_category.go
Executable file → Normal file
0
server/internal/dao/lesson_category.go
Executable file → Normal file
0
server/internal/dao/lesson_section.go
Executable file → Normal file
0
server/internal/dao/lesson_section.go
Executable file → Normal file
0
server/internal/dao/pay_log.go
Executable file → Normal file
0
server/internal/dao/pay_log.go
Executable file → Normal file
0
server/internal/dao/pay_refund.go
Executable file → Normal file
0
server/internal/dao/pay_refund.go
Executable file → Normal file
0
server/internal/dao/sys_addons_config.go
Executable file → Normal file
0
server/internal/dao/sys_addons_config.go
Executable file → Normal file
0
server/internal/dao/sys_attachment.go
Executable file → Normal file
0
server/internal/dao/sys_attachment.go
Executable file → Normal file
0
server/internal/dao/sys_blacklist.go
Executable file → Normal file
0
server/internal/dao/sys_blacklist.go
Executable file → Normal file
0
server/internal/dao/sys_config.go
Executable file → Normal file
0
server/internal/dao/sys_config.go
Executable file → Normal file
0
server/internal/dao/sys_cron.go
Executable file → Normal file
0
server/internal/dao/sys_cron.go
Executable file → Normal file
0
server/internal/dao/sys_cron_group.go
Executable file → Normal file
0
server/internal/dao/sys_cron_group.go
Executable file → Normal file
0
server/internal/dao/sys_dict_data.go
Executable file → Normal file
0
server/internal/dao/sys_dict_data.go
Executable file → Normal file
0
server/internal/dao/sys_dict_type.go
Executable file → Normal file
0
server/internal/dao/sys_dict_type.go
Executable file → Normal file
0
server/internal/dao/sys_ems_log.go
Executable file → Normal file
0
server/internal/dao/sys_ems_log.go
Executable file → Normal file
0
server/internal/dao/sys_gen_codes.go
Executable file → Normal file
0
server/internal/dao/sys_gen_codes.go
Executable file → Normal file
0
server/internal/dao/sys_gen_curd_demo.go
Executable file → Normal file
0
server/internal/dao/sys_gen_curd_demo.go
Executable file → Normal file
0
server/internal/dao/sys_gen_tree_demo.go
Executable file → Normal file
0
server/internal/dao/sys_gen_tree_demo.go
Executable file → Normal file
0
server/internal/dao/sys_log.go
Executable file → Normal file
0
server/internal/dao/sys_log.go
Executable file → Normal file
0
server/internal/dao/sys_login_log.go
Executable file → Normal file
0
server/internal/dao/sys_login_log.go
Executable file → Normal file
0
server/internal/dao/sys_provinces.go
Executable file → Normal file
0
server/internal/dao/sys_provinces.go
Executable file → Normal file
0
server/internal/dao/sys_serve_license.go
Executable file → Normal file
0
server/internal/dao/sys_serve_license.go
Executable file → Normal file
0
server/internal/dao/sys_serve_log.go
Executable file → Normal file
0
server/internal/dao/sys_serve_log.go
Executable file → Normal file
0
server/internal/dao/sys_sms_log.go
Executable file → Normal file
0
server/internal/dao/sys_sms_log.go
Executable file → Normal file
0
server/internal/dao/test_category.go
Executable file → Normal file
0
server/internal/dao/test_category.go
Executable file → Normal file
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user