1
This commit is contained in:
@@ -2,12 +2,14 @@ package v1
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/api/v1/example"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/api/v1/learning"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/api/v1/system"
|
||||
)
|
||||
|
||||
var ApiGroupApp = new(ApiGroup)
|
||||
|
||||
type ApiGroup struct {
|
||||
SystemApiGroup system.ApiGroup
|
||||
ExampleApiGroup example.ApiGroup
|
||||
SystemApiGroup system.ApiGroup
|
||||
ExampleApiGroup example.ApiGroup
|
||||
LearningApiGroup learning.ApiGroup
|
||||
}
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
package learning
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/learning"
|
||||
learningReq "github.com/flipped-aurora/gin-vue-admin/server/model/learning/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type ChapterApi struct{}
|
||||
|
||||
// CreateChapter 创建章节
|
||||
// @Tags Chapter
|
||||
// @Summary 创建章节
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body learningReq.ChapterCreate true "创建章节"
|
||||
// @Success 200 {object} response.Response{msg=string} "创建成功"
|
||||
// @Router /chapter/createChapter [post]
|
||||
func (chapterApi *ChapterApi) CreateChapter(c *gin.Context) {
|
||||
var req learningReq.ChapterCreate
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
chapter := learning.Chapter{
|
||||
CourseId: req.CourseId,
|
||||
Title: req.Title,
|
||||
Description: req.Description,
|
||||
Sort: req.Sort,
|
||||
Status: req.Status,
|
||||
Duration: req.Duration,
|
||||
}
|
||||
|
||||
err = service.ServiceGroupApp.LearningServiceGroup.ChapterService.CreateChapter(c.Request.Context(), &chapter)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("创建成功", c)
|
||||
}
|
||||
|
||||
// DeleteChapter 删除章节
|
||||
// @Tags Chapter
|
||||
// @Summary 删除章节
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.IdsReq true "批量删除章节"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除成功"
|
||||
// @Router /chapter/deleteChapter [delete]
|
||||
func (chapterApi *ChapterApi) DeleteChapter(c *gin.Context) {
|
||||
ID := c.Query("ID")
|
||||
err := service.ServiceGroupApp.LearningServiceGroup.ChapterService.DeleteChapter(c.Request.Context(), ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// DeleteChapterByIds 批量删除章节
|
||||
// @Tags Chapter
|
||||
// @Summary 批量删除章节
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{msg=string} "批量删除成功"
|
||||
// @Router /chapter/deleteChapterByIds [delete]
|
||||
func (chapterApi *ChapterApi) DeleteChapterByIds(c *gin.Context) {
|
||||
IDs := c.QueryArray("IDs[]")
|
||||
err := service.ServiceGroupApp.LearningServiceGroup.ChapterService.DeleteChapterByIds(c.Request.Context(), IDs)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("批量删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("批量删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("批量删除成功", c)
|
||||
}
|
||||
|
||||
// UpdateChapter 更新章节
|
||||
// @Tags Chapter
|
||||
// @Summary 更新章节
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body learningReq.ChapterUpdate true "更新章节"
|
||||
// @Success 200 {object} response.Response{msg=string} "更新成功"
|
||||
// @Router /chapter/updateChapter [put]
|
||||
func (chapterApi *ChapterApi) UpdateChapter(c *gin.Context) {
|
||||
var req learningReq.ChapterUpdate
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
chapter := learning.Chapter{
|
||||
GVA_MODEL: global.GVA_MODEL{ID: req.ID},
|
||||
CourseId: req.CourseId,
|
||||
Title: req.Title,
|
||||
Description: req.Description,
|
||||
Sort: req.Sort,
|
||||
Status: req.Status,
|
||||
Duration: req.Duration,
|
||||
}
|
||||
|
||||
err = service.ServiceGroupApp.LearningServiceGroup.ChapterService.UpdateChapter(c.Request.Context(), chapter)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("更新失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("更新成功", c)
|
||||
}
|
||||
|
||||
// FindChapter 用id查询章节
|
||||
// @Tags Chapter
|
||||
// @Summary 用id查询章节
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query learning.Chapter true "用id查询章节"
|
||||
// @Success 200 {object} response.Response{data=learning.Chapter,msg=string} "查询成功"
|
||||
// @Router /chapter/findChapter [get]
|
||||
func (chapterApi *ChapterApi) FindChapter(c *gin.Context) {
|
||||
ID := c.Query("ID")
|
||||
rechapter, err := service.ServiceGroupApp.LearningServiceGroup.ChapterService.GetChapter(c.Request.Context(), ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("查询失败!", zap.Error(err))
|
||||
response.FailWithMessage("查询失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(rechapter, c)
|
||||
}
|
||||
|
||||
// GetChapterList 分页获取章节列表
|
||||
// @Tags Chapter
|
||||
// @Summary 分页获取章节列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query learningReq.ChapterSearch true "分页获取章节列表"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取成功"
|
||||
// @Router /chapter/getChapterList [get]
|
||||
func (chapterApi *ChapterApi) GetChapterList(c *gin.Context) {
|
||||
var pageInfo learningReq.ChapterSearch
|
||||
err := c.ShouldBindQuery(&pageInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
list, total, err := service.ServiceGroupApp.LearningServiceGroup.ChapterService.GetChapterInfoList(c.Request.Context(), pageInfo)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: pageInfo.Page,
|
||||
PageSize: pageInfo.PageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
|
||||
// GetChaptersByCourse 根据课程获取章节列表
|
||||
// @Tags Chapter
|
||||
// @Summary 根据课程获取章节列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param courseId query uint true "课程ID"
|
||||
// @Success 200 {object} response.Response{data=[]learning.Chapter,msg=string} "获取成功"
|
||||
// @Router /chapter/getChaptersByCourse [get]
|
||||
func (chapterApi *ChapterApi) GetChaptersByCourse(c *gin.Context) {
|
||||
courseIdStr := c.Query("courseId")
|
||||
courseId, err := strconv.ParseUint(courseIdStr, 10, 32)
|
||||
if err != nil {
|
||||
response.FailWithMessage("课程ID格式错误", c)
|
||||
return
|
||||
}
|
||||
list, err := service.ServiceGroupApp.LearningServiceGroup.ChapterService.GetChaptersByCourse(c.Request.Context(), uint(courseId))
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(list, c)
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
package learning
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/learning"
|
||||
learningReq "github.com/flipped-aurora/gin-vue-admin/server/model/learning/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type CourseApi struct{}
|
||||
|
||||
// CreateCourse 创建课程
|
||||
// @Tags Course
|
||||
// @Summary 创建课程
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body learningReq.CourseCreate true "创建课程"
|
||||
// @Success 200 {object} response.Response{msg=string} "创建成功"
|
||||
// @Router /course/createCourse [post]
|
||||
func (courseApi *CourseApi) CreateCourse(c *gin.Context) {
|
||||
var req learningReq.CourseCreate
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
course := learning.Course{
|
||||
Title: req.Title,
|
||||
Description: req.Description,
|
||||
Cover: req.Cover,
|
||||
Difficulty: req.Difficulty,
|
||||
Duration: req.Duration,
|
||||
Status: req.Status,
|
||||
Price: req.Price,
|
||||
Tags: req.Tags,
|
||||
Sort: req.Sort,
|
||||
}
|
||||
|
||||
err = service.ServiceGroupApp.LearningServiceGroup.CourseService.CreateCourse(c.Request.Context(), &course)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("创建成功", c)
|
||||
}
|
||||
|
||||
// DeleteCourse 删除课程
|
||||
// @Tags Course
|
||||
// @Summary 删除课程
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.IdsReq true "批量删除课程"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除成功"
|
||||
// @Router /course/deleteCourse [delete]
|
||||
func (courseApi *CourseApi) DeleteCourse(c *gin.Context) {
|
||||
ID := c.Query("ID")
|
||||
err := service.ServiceGroupApp.LearningServiceGroup.CourseService.DeleteCourse(c.Request.Context(), ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// DeleteCourseByIds 批量删除课程
|
||||
// @Tags Course
|
||||
// @Summary 批量删除课程
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{msg=string} "批量删除成功"
|
||||
// @Router /course/deleteCourseByIds [delete]
|
||||
func (courseApi *CourseApi) DeleteCourseByIds(c *gin.Context) {
|
||||
IDs := c.QueryArray("IDs[]")
|
||||
err := service.ServiceGroupApp.LearningServiceGroup.CourseService.DeleteCourseByIds(c.Request.Context(), IDs)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("批量删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("批量删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("批量删除成功", c)
|
||||
}
|
||||
|
||||
// UpdateCourse 更新课程
|
||||
// @Tags Course
|
||||
// @Summary 更新课程
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body learningReq.CourseUpdate true "更新课程"
|
||||
// @Success 200 {object} response.Response{msg=string} "更新成功"
|
||||
// @Router /course/updateCourse [put]
|
||||
func (courseApi *CourseApi) UpdateCourse(c *gin.Context) {
|
||||
var req learningReq.CourseUpdate
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
course := learning.Course{
|
||||
GVA_MODEL: global.GVA_MODEL{ID: req.ID},
|
||||
Title: req.Title,
|
||||
Description: req.Description,
|
||||
Cover: req.Cover,
|
||||
Difficulty: req.Difficulty,
|
||||
Duration: req.Duration,
|
||||
Status: req.Status,
|
||||
Price: req.Price,
|
||||
Tags: req.Tags,
|
||||
Sort: req.Sort,
|
||||
}
|
||||
|
||||
err = service.ServiceGroupApp.LearningServiceGroup.CourseService.UpdateCourse(c.Request.Context(), course)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("更新失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("更新成功", c)
|
||||
}
|
||||
|
||||
// FindCourse 用id查询课程
|
||||
// @Tags Course
|
||||
// @Summary 用id查询课程
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query learning.Course true "用id查询课程"
|
||||
// @Success 200 {object} response.Response{data=learning.Course,msg=string} "查询成功"
|
||||
// @Router /course/findCourse [get]
|
||||
func (courseApi *CourseApi) FindCourse(c *gin.Context) {
|
||||
ID := c.Query("ID")
|
||||
recourse, err := service.ServiceGroupApp.LearningServiceGroup.CourseService.GetCourse(c.Request.Context(), ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("查询失败!", zap.Error(err))
|
||||
response.FailWithMessage("查询失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(recourse, c)
|
||||
}
|
||||
|
||||
// GetCourseList 分页获取课程列表
|
||||
// @Tags Course
|
||||
// @Summary 分页获取课程列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query learningReq.CourseSearch true "分页获取课程列表"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取成功"
|
||||
// @Router /course/getCourseList [get]
|
||||
func (courseApi *CourseApi) GetCourseList(c *gin.Context) {
|
||||
var pageInfo learningReq.CourseSearch
|
||||
err := c.ShouldBindQuery(&pageInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
list, total, err := service.ServiceGroupApp.LearningServiceGroup.CourseService.GetCourseInfoList(c.Request.Context(), pageInfo)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: pageInfo.Page,
|
||||
PageSize: pageInfo.PageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
|
||||
// GetPublishedCourses 获取已发布的课程列表
|
||||
// @Tags Course
|
||||
// @Summary 获取已发布的课程列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=[]learning.Course,msg=string} "获取成功"
|
||||
// @Router /course/getPublishedCourses [get]
|
||||
func (courseApi *CourseApi) GetPublishedCourses(c *gin.Context) {
|
||||
list, err := service.ServiceGroupApp.LearningServiceGroup.CourseService.GetPublishedCourses(c.Request.Context())
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(list, c)
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package learning
|
||||
|
||||
type ApiGroup struct {
|
||||
CourseApi
|
||||
ChapterApi
|
||||
KnowledgePointApi
|
||||
QuestionApi
|
||||
UserLearningApi
|
||||
WrongQuestionApi
|
||||
ExamApi
|
||||
UserExamApi
|
||||
}
|
||||
@@ -0,0 +1,276 @@
|
||||
package learning
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/learning"
|
||||
learningReq "github.com/flipped-aurora/gin-vue-admin/server/model/learning/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type ExamApi struct{}
|
||||
|
||||
// CreateExam 创建考试
|
||||
// @Tags Exam
|
||||
// @Summary 创建考试
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body learningReq.ExamCreate true "创建考试"
|
||||
// @Success 200 {object} response.Response{msg=string} "创建成功"
|
||||
// @Router /exam/createExam [post]
|
||||
func (examApi *ExamApi) CreateExam(c *gin.Context) {
|
||||
var req learningReq.ExamCreate
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
exam := learning.Exam{
|
||||
Title: req.Title,
|
||||
Description: req.Description,
|
||||
CourseId: req.CourseId,
|
||||
Duration: req.Duration,
|
||||
TotalScore: req.TotalScore,
|
||||
PassScore: req.PassScore,
|
||||
QuestionCount: req.QuestionCount,
|
||||
StartTime: req.StartTime,
|
||||
EndTime: req.EndTime,
|
||||
Status: req.Status,
|
||||
AllowRetake: req.AllowRetake,
|
||||
MaxAttempts: req.MaxAttempts,
|
||||
}
|
||||
|
||||
err = service.ServiceGroupApp.LearningServiceGroup.ExamService.CreateExam(c.Request.Context(), &exam)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("创建成功", c)
|
||||
}
|
||||
|
||||
// DeleteExam 删除考试
|
||||
// @Tags Exam
|
||||
// @Summary 删除考试
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.IdsReq true "批量删除考试"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除成功"
|
||||
// @Router /exam/deleteExam [delete]
|
||||
func (examApi *ExamApi) DeleteExam(c *gin.Context) {
|
||||
ID := c.Query("ID")
|
||||
err := service.ServiceGroupApp.LearningServiceGroup.ExamService.DeleteExam(c.Request.Context(), ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// DeleteExamByIds 批量删除考试
|
||||
// @Tags Exam
|
||||
// @Summary 批量删除考试
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{msg=string} "批量删除成功"
|
||||
// @Router /exam/deleteExamByIds [delete]
|
||||
func (examApi *ExamApi) DeleteExamByIds(c *gin.Context) {
|
||||
IDs := c.QueryArray("IDs[]")
|
||||
err := service.ServiceGroupApp.LearningServiceGroup.ExamService.DeleteExamByIds(c.Request.Context(), IDs)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("批量删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("批量删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("批量删除成功", c)
|
||||
}
|
||||
|
||||
// UpdateExam 更新考试
|
||||
// @Tags Exam
|
||||
// @Summary 更新考试
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body learningReq.ExamUpdate true "更新考试"
|
||||
// @Success 200 {object} response.Response{msg=string} "更新成功"
|
||||
// @Router /exam/updateExam [put]
|
||||
func (examApi *ExamApi) UpdateExam(c *gin.Context) {
|
||||
var req learningReq.ExamUpdate
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
exam := learning.Exam{
|
||||
GVA_MODEL: global.GVA_MODEL{ID: req.ID},
|
||||
Title: req.Title,
|
||||
Description: req.Description,
|
||||
CourseId: req.CourseId,
|
||||
Duration: req.Duration,
|
||||
TotalScore: req.TotalScore,
|
||||
PassScore: req.PassScore,
|
||||
QuestionCount: req.QuestionCount,
|
||||
StartTime: req.StartTime,
|
||||
EndTime: req.EndTime,
|
||||
Status: req.Status,
|
||||
AllowRetake: req.AllowRetake,
|
||||
MaxAttempts: req.MaxAttempts,
|
||||
}
|
||||
|
||||
err = service.ServiceGroupApp.LearningServiceGroup.ExamService.UpdateExam(c.Request.Context(), exam)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("更新失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("更新成功", c)
|
||||
}
|
||||
|
||||
// FindExam 用id查询考试
|
||||
// @Tags Exam
|
||||
// @Summary 用id查询考试
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query learning.Exam true "用id查询考试"
|
||||
// @Success 200 {object} response.Response{data=learning.Exam,msg=string} "查询成功"
|
||||
// @Router /exam/findExam [get]
|
||||
func (examApi *ExamApi) FindExam(c *gin.Context) {
|
||||
ID := c.Query("ID")
|
||||
reexam, err := service.ServiceGroupApp.LearningServiceGroup.ExamService.GetExam(c.Request.Context(), ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("查询失败!", zap.Error(err))
|
||||
response.FailWithMessage("查询失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(reexam, c)
|
||||
}
|
||||
|
||||
// GetExamList 分页获取考试列表
|
||||
// @Tags Exam
|
||||
// @Summary 分页获取考试列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query learningReq.ExamSearch true "分页获取考试列表"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取成功"
|
||||
// @Router /exam/getExamList [get]
|
||||
func (examApi *ExamApi) GetExamList(c *gin.Context) {
|
||||
var pageInfo learningReq.ExamSearch
|
||||
err := c.ShouldBindQuery(&pageInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
list, total, err := service.ServiceGroupApp.LearningServiceGroup.ExamService.GetExamInfoList(c.Request.Context(), pageInfo)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: pageInfo.Page,
|
||||
PageSize: pageInfo.PageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
|
||||
// GetActiveExams 获取进行中的考试列表
|
||||
// @Tags Exam
|
||||
// @Summary 获取进行中的考试列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=[]learning.Exam,msg=string} "获取成功"
|
||||
// @Router /exam/getActiveExams [get]
|
||||
func (examApi *ExamApi) GetActiveExams(c *gin.Context) {
|
||||
list, err := service.ServiceGroupApp.LearningServiceGroup.ExamService.GetActiveExams(c.Request.Context())
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(list, c)
|
||||
}
|
||||
|
||||
// GetExamsByCourse 根据课程获取考试列表
|
||||
// @Tags Exam
|
||||
// @Summary 根据课程获取考试列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param courseId query uint true "课程ID"
|
||||
// @Success 200 {object} response.Response{data=[]learning.Exam,msg=string} "获取成功"
|
||||
// @Router /exam/getExamsByCourse [get]
|
||||
func (examApi *ExamApi) GetExamsByCourse(c *gin.Context) {
|
||||
courseIdStr := c.Query("courseId")
|
||||
courseId := uint(0)
|
||||
if courseIdStr != "" {
|
||||
if id, err := strconv.ParseUint(courseIdStr, 10, 32); err == nil {
|
||||
courseId = uint(id)
|
||||
}
|
||||
}
|
||||
|
||||
list, err := service.ServiceGroupApp.LearningServiceGroup.ExamService.GetExamsByCourse(c.Request.Context(), courseId)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(list, c)
|
||||
}
|
||||
|
||||
// PublishExam 发布考试
|
||||
// @Tags Exam
|
||||
// @Summary 发布考试
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body learningReq.PublishExamRequest true "发布考试请求"
|
||||
// @Success 200 {object} response.Response{msg=string} "发布成功"
|
||||
// @Router /exam/publishExam [put]
|
||||
func (examApi *ExamApi) PublishExam(c *gin.Context) {
|
||||
var req learningReq.PublishExamRequest
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
err = service.ServiceGroupApp.LearningServiceGroup.ExamService.PublishExam(c.Request.Context(), req.ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("发布失败!", zap.Error(err))
|
||||
response.FailWithMessage("发布失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("发布成功", c)
|
||||
}
|
||||
|
||||
// GetExamStatistics 获取考试统计
|
||||
// @Tags Exam
|
||||
// @Summary 获取考试统计
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=learningReq.ExamStatistics,msg=string} "获取成功"
|
||||
// @Router /exam/getExamStatistics [get]
|
||||
func (examApi *ExamApi) GetExamStatistics(c *gin.Context) {
|
||||
stats, err := service.ServiceGroupApp.LearningServiceGroup.ExamService.GetExamStatistics(c.Request.Context())
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(stats, c)
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
package learning
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/learning"
|
||||
learningReq "github.com/flipped-aurora/gin-vue-admin/server/model/learning/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type KnowledgePointApi struct{}
|
||||
|
||||
// CreateKnowledgePoint 创建知识点
|
||||
// @Tags KnowledgePoint
|
||||
// @Summary 创建知识点
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body learningReq.KnowledgePointCreate true "创建知识点"
|
||||
// @Success 200 {object} response.Response{msg=string} "创建成功"
|
||||
// @Router /knowledgePoint/createKnowledgePoint [post]
|
||||
func (knowledgePointApi *KnowledgePointApi) CreateKnowledgePoint(c *gin.Context) {
|
||||
var req learningReq.KnowledgePointCreate
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
knowledgePoint := learning.KnowledgePoint{
|
||||
ChapterId: req.ChapterId,
|
||||
Title: req.Title,
|
||||
Content: req.Content,
|
||||
VideoUrl: req.VideoUrl,
|
||||
Attachments: req.Attachments,
|
||||
Sort: req.Sort,
|
||||
Duration: req.Duration,
|
||||
Status: req.Status,
|
||||
}
|
||||
|
||||
err = service.ServiceGroupApp.LearningServiceGroup.KnowledgePointService.CreateKnowledgePoint(c.Request.Context(), &knowledgePoint)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("创建成功", c)
|
||||
}
|
||||
|
||||
// DeleteKnowledgePoint 删除知识点
|
||||
// @Tags KnowledgePoint
|
||||
// @Summary 删除知识点
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.IdsReq true "批量删除知识点"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除成功"
|
||||
// @Router /knowledgePoint/deleteKnowledgePoint [delete]
|
||||
func (knowledgePointApi *KnowledgePointApi) DeleteKnowledgePoint(c *gin.Context) {
|
||||
ID := c.Query("ID")
|
||||
err := service.ServiceGroupApp.LearningServiceGroup.KnowledgePointService.DeleteKnowledgePoint(c.Request.Context(), ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// DeleteKnowledgePointByIds 批量删除知识点
|
||||
// @Tags KnowledgePoint
|
||||
// @Summary 批量删除知识点
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{msg=string} "批量删除成功"
|
||||
// @Router /knowledgePoint/deleteKnowledgePointByIds [delete]
|
||||
func (knowledgePointApi *KnowledgePointApi) DeleteKnowledgePointByIds(c *gin.Context) {
|
||||
IDs := c.QueryArray("IDs[]")
|
||||
err := service.ServiceGroupApp.LearningServiceGroup.KnowledgePointService.DeleteKnowledgePointByIds(c.Request.Context(), IDs)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("批量删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("批量删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("批量删除成功", c)
|
||||
}
|
||||
|
||||
// UpdateKnowledgePoint 更新知识点
|
||||
// @Tags KnowledgePoint
|
||||
// @Summary 更新知识点
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body learningReq.KnowledgePointUpdate true "更新知识点"
|
||||
// @Success 200 {object} response.Response{msg=string} "更新成功"
|
||||
// @Router /knowledgePoint/updateKnowledgePoint [put]
|
||||
func (knowledgePointApi *KnowledgePointApi) UpdateKnowledgePoint(c *gin.Context) {
|
||||
var req learningReq.KnowledgePointUpdate
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
knowledgePoint := learning.KnowledgePoint{
|
||||
GVA_MODEL: global.GVA_MODEL{ID: req.ID},
|
||||
ChapterId: req.ChapterId,
|
||||
Title: req.Title,
|
||||
Content: req.Content,
|
||||
VideoUrl: req.VideoUrl,
|
||||
Attachments: req.Attachments,
|
||||
Sort: req.Sort,
|
||||
Duration: req.Duration,
|
||||
Status: req.Status,
|
||||
}
|
||||
|
||||
err = service.ServiceGroupApp.LearningServiceGroup.KnowledgePointService.UpdateKnowledgePoint(c.Request.Context(), knowledgePoint)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("更新失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("更新成功", c)
|
||||
}
|
||||
|
||||
// FindKnowledgePoint 用id查询知识点
|
||||
// @Tags KnowledgePoint
|
||||
// @Summary 用id查询知识点
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query learning.KnowledgePoint true "用id查询知识点"
|
||||
// @Success 200 {object} response.Response{data=learning.KnowledgePoint,msg=string} "查询成功"
|
||||
// @Router /knowledgePoint/findKnowledgePoint [get]
|
||||
func (knowledgePointApi *KnowledgePointApi) FindKnowledgePoint(c *gin.Context) {
|
||||
ID := c.Query("ID")
|
||||
reknowledgePoint, err := service.ServiceGroupApp.LearningServiceGroup.KnowledgePointService.GetKnowledgePoint(c.Request.Context(), ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("查询失败!", zap.Error(err))
|
||||
response.FailWithMessage("查询失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(reknowledgePoint, c)
|
||||
}
|
||||
|
||||
// GetKnowledgePointList 分页获取知识点列表
|
||||
// @Tags KnowledgePoint
|
||||
// @Summary 分页获取知识点列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query learningReq.KnowledgePointSearch true "分页获取知识点列表"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取成功"
|
||||
// @Router /knowledgePoint/getKnowledgePointList [get]
|
||||
func (knowledgePointApi *KnowledgePointApi) GetKnowledgePointList(c *gin.Context) {
|
||||
var pageInfo learningReq.KnowledgePointSearch
|
||||
err := c.ShouldBindQuery(&pageInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
list, total, err := service.ServiceGroupApp.LearningServiceGroup.KnowledgePointService.GetKnowledgePointInfoList(c.Request.Context(), pageInfo)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: pageInfo.Page,
|
||||
PageSize: pageInfo.PageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
|
||||
// GetKnowledgePointsByChapter 根据章节获取知识点列表
|
||||
// @Tags KnowledgePoint
|
||||
// @Summary 根据章节获取知识点列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param chapterId query uint true "章节ID"
|
||||
// @Success 200 {object} response.Response{data=[]learning.KnowledgePoint,msg=string} "获取成功"
|
||||
// @Router /knowledgePoint/getKnowledgePointsByChapter [get]
|
||||
func (knowledgePointApi *KnowledgePointApi) GetKnowledgePointsByChapter(c *gin.Context) {
|
||||
chapterIdStr := c.Query("chapterId")
|
||||
chapterId, err := strconv.ParseUint(chapterIdStr, 10, 32)
|
||||
if err != nil {
|
||||
response.FailWithMessage("章节ID格式错误", c)
|
||||
return
|
||||
}
|
||||
list, err := service.ServiceGroupApp.LearningServiceGroup.KnowledgePointService.GetKnowledgePointsByChapter(c.Request.Context(), uint(chapterId))
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(list, c)
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
package learning
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/learning"
|
||||
learningReq "github.com/flipped-aurora/gin-vue-admin/server/model/learning/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type QuestionApi struct{}
|
||||
|
||||
// CreateQuestion 创建题目
|
||||
// @Tags Question
|
||||
// @Summary 创建题目
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body learningReq.QuestionCreate true "创建题目"
|
||||
// @Success 200 {object} response.Response{msg=string} "创建成功"
|
||||
// @Router /question/createQuestion [post]
|
||||
func (questionApi *QuestionApi) CreateQuestion(c *gin.Context) {
|
||||
var req learningReq.QuestionCreate
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
question := learning.Question{
|
||||
KnowledgePointId: req.KnowledgePointId,
|
||||
Title: req.Title,
|
||||
Content: req.Content,
|
||||
Type: req.Type,
|
||||
Options: req.Options,
|
||||
Answer: req.Answer,
|
||||
Explanation: req.Explanation,
|
||||
Difficulty: req.Difficulty,
|
||||
Score: req.Score,
|
||||
}
|
||||
|
||||
err = service.ServiceGroupApp.LearningServiceGroup.QuestionService.CreateQuestion(c.Request.Context(), &question)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("创建成功", c)
|
||||
}
|
||||
|
||||
// DeleteQuestion 删除题目
|
||||
// @Tags Question
|
||||
// @Summary 删除题目
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.IdsReq true "批量删除题目"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除成功"
|
||||
// @Router /question/deleteQuestion [delete]
|
||||
func (questionApi *QuestionApi) DeleteQuestion(c *gin.Context) {
|
||||
ID := c.Query("ID")
|
||||
err := service.ServiceGroupApp.LearningServiceGroup.QuestionService.DeleteQuestion(c.Request.Context(), ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// DeleteQuestionByIds 批量删除题目
|
||||
// @Tags Question
|
||||
// @Summary 批量删除题目
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{msg=string} "批量删除成功"
|
||||
// @Router /question/deleteQuestionByIds [delete]
|
||||
func (questionApi *QuestionApi) DeleteQuestionByIds(c *gin.Context) {
|
||||
IDs := c.QueryArray("IDs[]")
|
||||
err := service.ServiceGroupApp.LearningServiceGroup.QuestionService.DeleteQuestionByIds(c.Request.Context(), IDs)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("批量删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("批量删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("批量删除成功", c)
|
||||
}
|
||||
|
||||
// UpdateQuestion 更新题目
|
||||
// @Tags Question
|
||||
// @Summary 更新题目
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body learningReq.QuestionUpdate true "更新题目"
|
||||
// @Success 200 {object} response.Response{msg=string} "更新成功"
|
||||
// @Router /question/updateQuestion [put]
|
||||
func (questionApi *QuestionApi) UpdateQuestion(c *gin.Context) {
|
||||
var req learningReq.QuestionUpdate
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
question := learning.Question{
|
||||
GVA_MODEL: global.GVA_MODEL{ID: req.ID},
|
||||
KnowledgePointId: req.KnowledgePointId,
|
||||
Title: req.Title,
|
||||
Content: req.Content,
|
||||
Type: req.Type,
|
||||
Options: req.Options,
|
||||
Answer: req.Answer,
|
||||
Explanation: req.Explanation,
|
||||
Difficulty: req.Difficulty,
|
||||
Score: req.Score,
|
||||
}
|
||||
|
||||
err = service.ServiceGroupApp.LearningServiceGroup.QuestionService.UpdateQuestion(c.Request.Context(), question)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("更新失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("更新成功", c)
|
||||
}
|
||||
|
||||
// FindQuestion 用id查询题目
|
||||
// @Tags Question
|
||||
// @Summary 用id查询题目
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query learning.Question true "用id查询题目"
|
||||
// @Success 200 {object} response.Response{data=learning.Question,msg=string} "查询成功"
|
||||
// @Router /question/findQuestion [get]
|
||||
func (questionApi *QuestionApi) FindQuestion(c *gin.Context) {
|
||||
ID := c.Query("ID")
|
||||
requestion, err := service.ServiceGroupApp.LearningServiceGroup.QuestionService.GetQuestion(c.Request.Context(), ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("查询失败!", zap.Error(err))
|
||||
response.FailWithMessage("查询失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(requestion, c)
|
||||
}
|
||||
|
||||
// GetQuestionList 分页获取题目列表
|
||||
// @Tags Question
|
||||
// @Summary 分页获取题目列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query learningReq.QuestionSearch true "分页获取题目列表"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取成功"
|
||||
// @Router /question/getQuestionList [get]
|
||||
func (questionApi *QuestionApi) GetQuestionList(c *gin.Context) {
|
||||
var pageInfo learningReq.QuestionSearch
|
||||
err := c.ShouldBindQuery(&pageInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
list, total, err := service.ServiceGroupApp.LearningServiceGroup.QuestionService.GetQuestionInfoList(c.Request.Context(), pageInfo)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: pageInfo.Page,
|
||||
PageSize: pageInfo.PageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
|
||||
// GetQuestionsByKnowledgePoint 根据知识点获取题目列表
|
||||
// @Tags Question
|
||||
// @Summary 根据知识点获取题目列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param knowledgePointId query uint true "知识点ID"
|
||||
// @Success 200 {object} response.Response{data=[]learning.Question,msg=string} "获取成功"
|
||||
// @Router /question/getQuestionsByKnowledgePoint [get]
|
||||
func (questionApi *QuestionApi) GetQuestionsByKnowledgePoint(c *gin.Context) {
|
||||
knowledgePointIdStr := c.Query("knowledgePointId")
|
||||
knowledgePointId, err := strconv.ParseUint(knowledgePointIdStr, 10, 32)
|
||||
if err != nil {
|
||||
response.FailWithMessage("知识点ID格式错误", c)
|
||||
return
|
||||
}
|
||||
list, err := service.ServiceGroupApp.LearningServiceGroup.QuestionService.GetQuestionsByKnowledgePoint(c.Request.Context(), uint(knowledgePointId))
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(list, c)
|
||||
}
|
||||
@@ -0,0 +1,349 @@
|
||||
package learning
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/learning"
|
||||
learningReq "github.com/flipped-aurora/gin-vue-admin/server/model/learning/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/service"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type UserExamApi struct{}
|
||||
|
||||
// CreateUserExam 创建用户考试记录
|
||||
// @Tags UserExam
|
||||
// @Summary 创建用户考试记录
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body learningReq.UserExamCreate true "创建用户考试记录"
|
||||
// @Success 200 {object} response.Response{msg=string} "创建成功"
|
||||
// @Router /userExam/createUserExam [post]
|
||||
func (userExamApi *UserExamApi) CreateUserExam(c *gin.Context) {
|
||||
var req learningReq.UserExamCreate
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
userExam := learning.UserExam{
|
||||
UserId: req.UserId,
|
||||
ExamId: req.ExamId,
|
||||
AttemptNumber: req.AttemptNumber,
|
||||
StartTime: req.StartTime,
|
||||
EndTime: req.EndTime,
|
||||
Duration: req.Duration,
|
||||
TotalScore: req.TotalScore,
|
||||
CorrectCount: req.CorrectCount,
|
||||
WrongCount: req.WrongCount,
|
||||
Status: req.Status,
|
||||
IsPassed: req.IsPassed,
|
||||
SubmittedAt: req.SubmittedAt,
|
||||
}
|
||||
|
||||
err = service.ServiceGroupApp.LearningServiceGroup.UserExamService.CreateUserExam(c.Request.Context(), &userExam)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("创建成功", c)
|
||||
}
|
||||
|
||||
// DeleteUserExam 删除用户考试记录
|
||||
// @Tags UserExam
|
||||
// @Summary 删除用户考试记录
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.IdsReq true "批量删除用户考试记录"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除成功"
|
||||
// @Router /userExam/deleteUserExam [delete]
|
||||
func (userExamApi *UserExamApi) DeleteUserExam(c *gin.Context) {
|
||||
ID := c.Query("ID")
|
||||
err := service.ServiceGroupApp.LearningServiceGroup.UserExamService.DeleteUserExam(c.Request.Context(), ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// DeleteUserExamByIds 批量删除用户考试记录
|
||||
// @Tags UserExam
|
||||
// @Summary 批量删除用户考试记录
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{msg=string} "批量删除成功"
|
||||
// @Router /userExam/deleteUserExamByIds [delete]
|
||||
func (userExamApi *UserExamApi) DeleteUserExamByIds(c *gin.Context) {
|
||||
IDs := c.QueryArray("IDs[]")
|
||||
err := service.ServiceGroupApp.LearningServiceGroup.UserExamService.DeleteUserExamByIds(c.Request.Context(), IDs)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("批量删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("批量删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("批量删除成功", c)
|
||||
}
|
||||
|
||||
// UpdateUserExam 更新用户考试记录
|
||||
// @Tags UserExam
|
||||
// @Summary 更新用户考试记录
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body learningReq.UserExamUpdate true "更新用户考试记录"
|
||||
// @Success 200 {object} response.Response{msg=string} "更新成功"
|
||||
// @Router /userExam/updateUserExam [put]
|
||||
func (userExamApi *UserExamApi) UpdateUserExam(c *gin.Context) {
|
||||
var req learningReq.UserExamUpdate
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
userExam := learning.UserExam{
|
||||
GVA_MODEL: global.GVA_MODEL{ID: req.ID},
|
||||
UserId: req.UserId,
|
||||
ExamId: req.ExamId,
|
||||
AttemptNumber: req.AttemptNumber,
|
||||
StartTime: req.StartTime,
|
||||
EndTime: req.EndTime,
|
||||
Duration: req.Duration,
|
||||
TotalScore: req.TotalScore,
|
||||
CorrectCount: req.CorrectCount,
|
||||
WrongCount: req.WrongCount,
|
||||
Status: req.Status,
|
||||
IsPassed: req.IsPassed,
|
||||
SubmittedAt: req.SubmittedAt,
|
||||
}
|
||||
|
||||
err = service.ServiceGroupApp.LearningServiceGroup.UserExamService.UpdateUserExam(c.Request.Context(), userExam)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("更新失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("更新成功", c)
|
||||
}
|
||||
|
||||
// FindUserExam 用id查询用户考试记录
|
||||
// @Tags UserExam
|
||||
// @Summary 用id查询用户考试记录
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query learning.UserExam true "用id查询用户考试记录"
|
||||
// @Success 200 {object} response.Response{data=learning.UserExam,msg=string} "查询成功"
|
||||
// @Router /userExam/findUserExam [get]
|
||||
func (userExamApi *UserExamApi) FindUserExam(c *gin.Context) {
|
||||
ID := c.Query("ID")
|
||||
reuserExam, err := service.ServiceGroupApp.LearningServiceGroup.UserExamService.GetUserExam(c.Request.Context(), ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("查询失败!", zap.Error(err))
|
||||
response.FailWithMessage("查询失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(reuserExam, c)
|
||||
}
|
||||
|
||||
// GetUserExamList 分页获取用户考试记录列表
|
||||
// @Tags UserExam
|
||||
// @Summary 分页获取用户考试记录列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query learningReq.UserExamSearch true "分页获取用户考试记录列表"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取成功"
|
||||
// @Router /userExam/getUserExamList [get]
|
||||
func (userExamApi *UserExamApi) GetUserExamList(c *gin.Context) {
|
||||
var pageInfo learningReq.UserExamSearch
|
||||
err := c.ShouldBindQuery(&pageInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
list, total, err := service.ServiceGroupApp.LearningServiceGroup.UserExamService.GetUserExamInfoList(c.Request.Context(), pageInfo)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: pageInfo.Page,
|
||||
PageSize: pageInfo.PageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
|
||||
// StartExam 开始考试
|
||||
// @Tags UserExam
|
||||
// @Summary 开始考试
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body learningReq.StartExamRequest true "开始考试请求"
|
||||
// @Success 200 {object} response.Response{data=learning.UserExam,msg=string} "开始成功"
|
||||
// @Router /userExam/startExam [post]
|
||||
func (userExamApi *UserExamApi) StartExam(c *gin.Context) {
|
||||
var req learningReq.StartExamRequest
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
// 如果没有提供userId,从JWT中获取当前用户ID
|
||||
if req.UserId == 0 {
|
||||
req.UserId = uint(utils.GetUserID(c))
|
||||
}
|
||||
|
||||
userExam, err := service.ServiceGroupApp.LearningServiceGroup.UserExamService.StartExam(c.Request.Context(), req.UserId, req.ExamId)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("开始考试失败!", zap.Error(err))
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(userExam, c)
|
||||
}
|
||||
|
||||
// SubmitExam 提交考试
|
||||
// @Tags UserExam
|
||||
// @Summary 提交考试
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body learningReq.SubmitExamRequest true "提交考试请求"
|
||||
// @Success 200 {object} response.Response{msg=string} "提交成功"
|
||||
// @Router /userExam/submitExam [put]
|
||||
func (userExamApi *UserExamApi) SubmitExam(c *gin.Context) {
|
||||
var req learningReq.SubmitExamRequest
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
err = service.ServiceGroupApp.LearningServiceGroup.UserExamService.SubmitExam(c.Request.Context(), req.UserExamId, req.TotalScore, req.CorrectCount, req.WrongCount)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("提交考试失败!", zap.Error(err))
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("提交成功", c)
|
||||
}
|
||||
|
||||
// GetUserExamHistory 获取用户考试历史
|
||||
// @Tags UserExam
|
||||
// @Summary 获取用户考试历史
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param userId query uint false "用户ID"
|
||||
// @Success 200 {object} response.Response{data=[]learning.UserExam,msg=string} "获取成功"
|
||||
// @Router /userExam/getUserExamHistory [get]
|
||||
func (userExamApi *UserExamApi) GetUserExamHistory(c *gin.Context) {
|
||||
userIdStr := c.Query("userId")
|
||||
|
||||
var userId uint
|
||||
if userIdStr != "" {
|
||||
userIdUint, err := strconv.ParseUint(userIdStr, 10, 32)
|
||||
if err != nil {
|
||||
response.FailWithMessage("用户ID格式错误", c)
|
||||
return
|
||||
}
|
||||
userId = uint(userIdUint)
|
||||
} else {
|
||||
// 如果没有提供userId,从JWT中获取当前用户ID
|
||||
userId = uint(utils.GetUserID(c))
|
||||
}
|
||||
|
||||
list, err := service.ServiceGroupApp.LearningServiceGroup.UserExamService.GetUserExamHistory(c.Request.Context(), userId)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(list, c)
|
||||
}
|
||||
|
||||
// GetUserExamStatistics 获取用户考试统计
|
||||
// @Tags UserExam
|
||||
// @Summary 获取用户考试统计
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param userId query uint false "用户ID"
|
||||
// @Success 200 {object} response.Response{data=learningReq.UserExamStatistics,msg=string} "获取成功"
|
||||
// @Router /userExam/getUserExamStatistics [get]
|
||||
func (userExamApi *UserExamApi) GetUserExamStatistics(c *gin.Context) {
|
||||
userIdStr := c.Query("userId")
|
||||
|
||||
var userId uint
|
||||
if userIdStr != "" {
|
||||
userIdUint, err := strconv.ParseUint(userIdStr, 10, 32)
|
||||
if err != nil {
|
||||
response.FailWithMessage("用户ID格式错误", c)
|
||||
return
|
||||
}
|
||||
userId = uint(userIdUint)
|
||||
} else {
|
||||
// 如果没有提供userId,从JWT中获取当前用户ID
|
||||
userId = uint(utils.GetUserID(c))
|
||||
}
|
||||
|
||||
stats, err := service.ServiceGroupApp.LearningServiceGroup.UserExamService.GetUserExamStatistics(c.Request.Context(), userId)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(stats, c)
|
||||
}
|
||||
|
||||
// GetExamRanking 获取考试排行榜
|
||||
// @Tags UserExam
|
||||
// @Summary 获取考试排行榜
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param examId query uint true "考试ID"
|
||||
// @Param limit query int false "限制数量"
|
||||
// @Success 200 {object} response.Response{data=learningReq.ExamRankingResponse,msg=string} "获取成功"
|
||||
// @Router /userExam/getExamRanking [get]
|
||||
func (userExamApi *UserExamApi) GetExamRanking(c *gin.Context) {
|
||||
examIdStr := c.Query("examId")
|
||||
limitStr := c.Query("limit")
|
||||
|
||||
examId, err := strconv.ParseUint(examIdStr, 10, 32)
|
||||
if err != nil {
|
||||
response.FailWithMessage("考试ID格式错误", c)
|
||||
return
|
||||
}
|
||||
|
||||
limit := 10 // 默认限制10条
|
||||
if limitStr != "" {
|
||||
if l, err := strconv.Atoi(limitStr); err == nil {
|
||||
limit = l
|
||||
}
|
||||
}
|
||||
|
||||
ranking, err := service.ServiceGroupApp.LearningServiceGroup.UserExamService.GetExamRanking(c.Request.Context(), uint(examId), limit)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(ranking, c)
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
package learning
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/learning"
|
||||
learningReq "github.com/flipped-aurora/gin-vue-admin/server/model/learning/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/service"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type UserLearningApi struct{}
|
||||
|
||||
// CreateUserLearning 创建用户学习记录
|
||||
// @Tags UserLearning
|
||||
// @Summary 创建用户学习记录
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body learningReq.UserLearningCreate true "创建用户学习记录"
|
||||
// @Success 200 {object} response.Response{msg=string} "创建成功"
|
||||
// @Router /userLearning/createUserLearning [post]
|
||||
func (userLearningApi *UserLearningApi) CreateUserLearning(c *gin.Context) {
|
||||
var req learningReq.UserLearningCreate
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
userLearning := learning.UserLearning{
|
||||
UserId: req.UserId,
|
||||
CourseId: req.CourseId,
|
||||
ChapterId: req.ChapterId,
|
||||
KnowledgePointId: req.KnowledgePointId,
|
||||
Progress: req.Progress,
|
||||
StudyTime: req.StudyTime,
|
||||
LastStudyAt: req.LastStudyAt,
|
||||
Status: req.Status,
|
||||
Score: req.Score,
|
||||
CompletedAt: req.CompletedAt,
|
||||
}
|
||||
|
||||
err = service.ServiceGroupApp.LearningServiceGroup.UserLearningService.CreateUserLearning(c.Request.Context(), &userLearning)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("创建成功", c)
|
||||
}
|
||||
|
||||
// DeleteUserLearning 删除用户学习记录
|
||||
// @Tags UserLearning
|
||||
// @Summary 删除用户学习记录
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.IdsReq true "批量删除用户学习记录"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除成功"
|
||||
// @Router /userLearning/deleteUserLearning [delete]
|
||||
func (userLearningApi *UserLearningApi) DeleteUserLearning(c *gin.Context) {
|
||||
ID := c.Query("ID")
|
||||
err := service.ServiceGroupApp.LearningServiceGroup.UserLearningService.DeleteUserLearning(c.Request.Context(), ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// DeleteUserLearningByIds 批量删除用户学习记录
|
||||
// @Tags UserLearning
|
||||
// @Summary 批量删除用户学习记录
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{msg=string} "批量删除成功"
|
||||
// @Router /userLearning/deleteUserLearningByIds [delete]
|
||||
func (userLearningApi *UserLearningApi) DeleteUserLearningByIds(c *gin.Context) {
|
||||
IDs := c.QueryArray("IDs[]")
|
||||
err := service.ServiceGroupApp.LearningServiceGroup.UserLearningService.DeleteUserLearningByIds(c.Request.Context(), IDs)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("批量删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("批量删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("批量删除成功", c)
|
||||
}
|
||||
|
||||
// UpdateUserLearning 更新用户学习记录
|
||||
// @Tags UserLearning
|
||||
// @Summary 更新用户学习记录
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body learningReq.UserLearningUpdate true "更新用户学习记录"
|
||||
// @Success 200 {object} response.Response{msg=string} "更新成功"
|
||||
// @Router /userLearning/updateUserLearning [put]
|
||||
func (userLearningApi *UserLearningApi) UpdateUserLearning(c *gin.Context) {
|
||||
var req learningReq.UserLearningUpdate
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
userLearning := learning.UserLearning{
|
||||
GVA_MODEL: global.GVA_MODEL{ID: req.ID},
|
||||
UserId: req.UserId,
|
||||
CourseId: req.CourseId,
|
||||
ChapterId: req.ChapterId,
|
||||
KnowledgePointId: req.KnowledgePointId,
|
||||
Progress: req.Progress,
|
||||
StudyTime: req.StudyTime,
|
||||
LastStudyAt: req.LastStudyAt,
|
||||
Status: req.Status,
|
||||
Score: req.Score,
|
||||
CompletedAt: req.CompletedAt,
|
||||
}
|
||||
|
||||
err = service.ServiceGroupApp.LearningServiceGroup.UserLearningService.UpdateUserLearning(c.Request.Context(), userLearning)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("更新失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("更新成功", c)
|
||||
}
|
||||
|
||||
// FindUserLearning 用id查询用户学习记录
|
||||
// @Tags UserLearning
|
||||
// @Summary 用id查询用户学习记录
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query learning.UserLearning true "用id查询用户学习记录"
|
||||
// @Success 200 {object} response.Response{data=learning.UserLearning,msg=string} "查询成功"
|
||||
// @Router /userLearning/findUserLearning [get]
|
||||
func (userLearningApi *UserLearningApi) FindUserLearning(c *gin.Context) {
|
||||
ID := c.Query("ID")
|
||||
reuserLearning, err := service.ServiceGroupApp.LearningServiceGroup.UserLearningService.GetUserLearning(c.Request.Context(), ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("查询失败!", zap.Error(err))
|
||||
response.FailWithMessage("查询失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(reuserLearning, c)
|
||||
}
|
||||
|
||||
// GetUserLearningList 分页获取用户学习记录列表
|
||||
// @Tags UserLearning
|
||||
// @Summary 分页获取用户学习记录列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query learningReq.UserLearningSearch true "分页获取用户学习记录列表"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取成功"
|
||||
// @Router /userLearning/getUserLearningList [get]
|
||||
func (userLearningApi *UserLearningApi) GetUserLearningList(c *gin.Context) {
|
||||
var pageInfo learningReq.UserLearningSearch
|
||||
err := c.ShouldBindQuery(&pageInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
list, total, err := service.ServiceGroupApp.LearningServiceGroup.UserLearningService.GetUserLearningInfoList(c.Request.Context(), pageInfo)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: pageInfo.Page,
|
||||
PageSize: pageInfo.PageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
|
||||
// GetUserProgress 获取用户学习进度
|
||||
// @Tags UserLearning
|
||||
// @Summary 获取用户学习进度
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param userId query uint true "用户ID"
|
||||
// @Param courseId query uint false "课程ID"
|
||||
// @Success 200 {object} response.Response{data=[]learning.UserLearning,msg=string} "获取成功"
|
||||
// @Router /userLearning/getUserProgress [get]
|
||||
func (userLearningApi *UserLearningApi) GetUserProgress(c *gin.Context) {
|
||||
userIdStr := c.Query("userId")
|
||||
courseIdStr := c.Query("courseId")
|
||||
|
||||
userId, err := strconv.ParseUint(userIdStr, 10, 32)
|
||||
if err != nil {
|
||||
response.FailWithMessage("用户ID格式错误", c)
|
||||
return
|
||||
}
|
||||
|
||||
var courseId uint = 0
|
||||
if courseIdStr != "" {
|
||||
courseIdUint, err := strconv.ParseUint(courseIdStr, 10, 32)
|
||||
if err != nil {
|
||||
response.FailWithMessage("课程ID格式错误", c)
|
||||
return
|
||||
}
|
||||
courseId = uint(courseIdUint)
|
||||
}
|
||||
|
||||
// 如果没有提供userId,尝试从JWT中获取当前用户ID
|
||||
if userId == 0 {
|
||||
userId = uint64(utils.GetUserID(c))
|
||||
}
|
||||
|
||||
list, err := service.ServiceGroupApp.LearningServiceGroup.UserLearningService.GetUserProgress(c.Request.Context(), uint(userId), courseId)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(list, c)
|
||||
}
|
||||
|
||||
// GetLearningStatistics 获取用户学习统计
|
||||
// @Tags UserLearning
|
||||
// @Summary 获取用户学习统计
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param userId query uint false "用户ID"
|
||||
// @Success 200 {object} response.Response{data=learningReq.LearningStatistics,msg=string} "获取成功"
|
||||
// @Router /userLearning/getLearningStatistics [get]
|
||||
func (userLearningApi *UserLearningApi) GetLearningStatistics(c *gin.Context) {
|
||||
userIdStr := c.Query("userId")
|
||||
|
||||
var userId uint
|
||||
if userIdStr != "" {
|
||||
userIdUint, err := strconv.ParseUint(userIdStr, 10, 32)
|
||||
if err != nil {
|
||||
response.FailWithMessage("用户ID格式错误", c)
|
||||
return
|
||||
}
|
||||
userId = uint(userIdUint)
|
||||
} else {
|
||||
// 如果没有提供userId,从JWT中获取当前用户ID
|
||||
userId = uint(utils.GetUserID(c))
|
||||
}
|
||||
|
||||
stats, err := service.ServiceGroupApp.LearningServiceGroup.UserLearningService.GetLearningStatistics(c.Request.Context(), userId)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(stats, c)
|
||||
}
|
||||
@@ -0,0 +1,278 @@
|
||||
package learning
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/learning"
|
||||
learningReq "github.com/flipped-aurora/gin-vue-admin/server/model/learning/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/service"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type WrongQuestionApi struct{}
|
||||
|
||||
// CreateWrongQuestion 创建错题记录
|
||||
// @Tags WrongQuestion
|
||||
// @Summary 创建错题记录
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body learningReq.WrongQuestionCreate true "创建错题记录"
|
||||
// @Success 200 {object} response.Response{msg=string} "创建成功"
|
||||
// @Router /wrongQuestion/createWrongQuestion [post]
|
||||
func (wrongQuestionApi *WrongQuestionApi) CreateWrongQuestion(c *gin.Context) {
|
||||
var req learningReq.WrongQuestionCreate
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
wrongQuestion := learning.WrongQuestion{
|
||||
UserId: req.UserId,
|
||||
QuestionId: req.QuestionId,
|
||||
WrongAnswer: req.WrongAnswer,
|
||||
WrongCount: req.WrongCount,
|
||||
LastWrongAt: req.LastWrongAt,
|
||||
IsMastered: req.IsMastered,
|
||||
MasteredAt: req.MasteredAt,
|
||||
Notes: req.Notes,
|
||||
}
|
||||
|
||||
err = service.ServiceGroupApp.LearningServiceGroup.WrongQuestionService.CreateWrongQuestion(c.Request.Context(), &wrongQuestion)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("创建成功", c)
|
||||
}
|
||||
|
||||
// DeleteWrongQuestion 删除错题记录
|
||||
// @Tags WrongQuestion
|
||||
// @Summary 删除错题记录
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.IdsReq true "批量删除错题记录"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除成功"
|
||||
// @Router /wrongQuestion/deleteWrongQuestion [delete]
|
||||
func (wrongQuestionApi *WrongQuestionApi) DeleteWrongQuestion(c *gin.Context) {
|
||||
ID := c.Query("ID")
|
||||
err := service.ServiceGroupApp.LearningServiceGroup.WrongQuestionService.DeleteWrongQuestion(c.Request.Context(), ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// DeleteWrongQuestionByIds 批量删除错题记录
|
||||
// @Tags WrongQuestion
|
||||
// @Summary 批量删除错题记录
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{msg=string} "批量删除成功"
|
||||
// @Router /wrongQuestion/deleteWrongQuestionByIds [delete]
|
||||
func (wrongQuestionApi *WrongQuestionApi) DeleteWrongQuestionByIds(c *gin.Context) {
|
||||
IDs := c.QueryArray("IDs[]")
|
||||
err := service.ServiceGroupApp.LearningServiceGroup.WrongQuestionService.DeleteWrongQuestionByIds(c.Request.Context(), IDs)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("批量删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("批量删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("批量删除成功", c)
|
||||
}
|
||||
|
||||
// UpdateWrongQuestion 更新错题记录
|
||||
// @Tags WrongQuestion
|
||||
// @Summary 更新错题记录
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body learningReq.WrongQuestionUpdate true "更新错题记录"
|
||||
// @Success 200 {object} response.Response{msg=string} "更新成功"
|
||||
// @Router /wrongQuestion/updateWrongQuestion [put]
|
||||
func (wrongQuestionApi *WrongQuestionApi) UpdateWrongQuestion(c *gin.Context) {
|
||||
var req learningReq.WrongQuestionUpdate
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
wrongQuestion := learning.WrongQuestion{
|
||||
GVA_MODEL: global.GVA_MODEL{ID: req.ID},
|
||||
UserId: req.UserId,
|
||||
QuestionId: req.QuestionId,
|
||||
WrongAnswer: req.WrongAnswer,
|
||||
WrongCount: req.WrongCount,
|
||||
LastWrongAt: req.LastWrongAt,
|
||||
IsMastered: req.IsMastered,
|
||||
MasteredAt: req.MasteredAt,
|
||||
Notes: req.Notes,
|
||||
}
|
||||
|
||||
err = service.ServiceGroupApp.LearningServiceGroup.WrongQuestionService.UpdateWrongQuestion(c.Request.Context(), wrongQuestion)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("更新失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("更新成功", c)
|
||||
}
|
||||
|
||||
// FindWrongQuestion 用id查询错题记录
|
||||
// @Tags WrongQuestion
|
||||
// @Summary 用id查询错题记录
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query learning.WrongQuestion true "用id查询错题记录"
|
||||
// @Success 200 {object} response.Response{data=learning.WrongQuestion,msg=string} "查询成功"
|
||||
// @Router /wrongQuestion/findWrongQuestion [get]
|
||||
func (wrongQuestionApi *WrongQuestionApi) FindWrongQuestion(c *gin.Context) {
|
||||
ID := c.Query("ID")
|
||||
rewrongQuestion, err := service.ServiceGroupApp.LearningServiceGroup.WrongQuestionService.GetWrongQuestion(c.Request.Context(), ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("查询失败!", zap.Error(err))
|
||||
response.FailWithMessage("查询失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(rewrongQuestion, c)
|
||||
}
|
||||
|
||||
// GetWrongQuestionList 分页获取错题记录列表
|
||||
// @Tags WrongQuestion
|
||||
// @Summary 分页获取错题记录列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query learningReq.WrongQuestionSearch true "分页获取错题记录列表"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取成功"
|
||||
// @Router /wrongQuestion/getWrongQuestionList [get]
|
||||
func (wrongQuestionApi *WrongQuestionApi) GetWrongQuestionList(c *gin.Context) {
|
||||
var pageInfo learningReq.WrongQuestionSearch
|
||||
err := c.ShouldBindQuery(&pageInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
list, total, err := service.ServiceGroupApp.LearningServiceGroup.WrongQuestionService.GetWrongQuestionInfoList(c.Request.Context(), pageInfo)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: pageInfo.Page,
|
||||
PageSize: pageInfo.PageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
|
||||
// GetUserWrongQuestions 获取用户错题列表
|
||||
// @Tags WrongQuestion
|
||||
// @Summary 获取用户错题列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param userId query uint false "用户ID"
|
||||
// @Param onlyUnmastered query bool false "只获取未掌握的题目"
|
||||
// @Success 200 {object} response.Response{data=[]learning.WrongQuestion,msg=string} "获取成功"
|
||||
// @Router /wrongQuestion/getUserWrongQuestions [get]
|
||||
func (wrongQuestionApi *WrongQuestionApi) GetUserWrongQuestions(c *gin.Context) {
|
||||
userIdStr := c.Query("userId")
|
||||
onlyUnmasteredStr := c.Query("onlyUnmastered")
|
||||
|
||||
var userId uint
|
||||
if userIdStr != "" {
|
||||
userIdUint, err := strconv.ParseUint(userIdStr, 10, 32)
|
||||
if err != nil {
|
||||
response.FailWithMessage("用户ID格式错误", c)
|
||||
return
|
||||
}
|
||||
userId = uint(userIdUint)
|
||||
} else {
|
||||
// 如果没有提供userId,从JWT中获取当前用户ID
|
||||
userId = uint(utils.GetUserID(c))
|
||||
}
|
||||
|
||||
onlyUnmastered := onlyUnmasteredStr == "true"
|
||||
|
||||
list, err := service.ServiceGroupApp.LearningServiceGroup.WrongQuestionService.GetUserWrongQuestions(c.Request.Context(), userId, onlyUnmastered)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(list, c)
|
||||
}
|
||||
|
||||
// MarkAsMastered 标记题目为已掌握
|
||||
// @Tags WrongQuestion
|
||||
// @Summary 标记题目为已掌握
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body learningReq.MarkMasteredRequest true "标记掌握请求"
|
||||
// @Success 200 {object} response.Response{msg=string} "标记成功"
|
||||
// @Router /wrongQuestion/markAsMastered [put]
|
||||
func (wrongQuestionApi *WrongQuestionApi) MarkAsMastered(c *gin.Context) {
|
||||
var req learningReq.MarkMasteredRequest
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
err = service.ServiceGroupApp.LearningServiceGroup.WrongQuestionService.MarkAsMastered(c.Request.Context(), req.UserId, req.ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("标记失败!", zap.Error(err))
|
||||
response.FailWithMessage("标记失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("标记成功", c)
|
||||
}
|
||||
|
||||
// GetWrongQuestionStatistics 获取错题统计
|
||||
// @Tags WrongQuestion
|
||||
// @Summary 获取错题统计
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param userId query uint false "用户ID"
|
||||
// @Success 200 {object} response.Response{data=learningReq.WrongQuestionStatistics,msg=string} "获取成功"
|
||||
// @Router /wrongQuestion/getWrongQuestionStatistics [get]
|
||||
func (wrongQuestionApi *WrongQuestionApi) GetWrongQuestionStatistics(c *gin.Context) {
|
||||
userIdStr := c.Query("userId")
|
||||
|
||||
var userId uint
|
||||
if userIdStr != "" {
|
||||
userIdUint, err := strconv.ParseUint(userIdStr, 10, 32)
|
||||
if err != nil {
|
||||
response.FailWithMessage("用户ID格式错误", c)
|
||||
return
|
||||
}
|
||||
userId = uint(userIdUint)
|
||||
} else {
|
||||
// 如果没有提供userId,从JWT中获取当前用户ID
|
||||
userId = uint(utils.GetUserID(c))
|
||||
}
|
||||
|
||||
stats, err := service.ServiceGroupApp.LearningServiceGroup.WrongQuestionService.GetWrongQuestionStatistics(c.Request.Context(), userId)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(stats, c)
|
||||
}
|
||||
@@ -2,11 +2,22 @@ package initialize
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/learning"
|
||||
)
|
||||
|
||||
func bizModel() error {
|
||||
db := global.GVA_DB
|
||||
err := db.AutoMigrate()
|
||||
err := db.AutoMigrate(
|
||||
// Learning 学习系统模型
|
||||
learning.Course{},
|
||||
learning.Chapter{},
|
||||
learning.KnowledgePoint{},
|
||||
learning.Question{},
|
||||
learning.UserLearning{},
|
||||
learning.WrongQuestion{},
|
||||
learning.Exam{},
|
||||
learning.UserExam{},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -15,5 +15,18 @@ func initBizRouter(routers ...*gin.RouterGroup) {
|
||||
privateGroup := routers[0]
|
||||
publicGroup := routers[1]
|
||||
|
||||
// Learning 学习系统路由
|
||||
learningRouter := router.RouterGroupApp.Learning
|
||||
{
|
||||
learningRouter.InitCourseRouter(privateGroup, publicGroup) // 课程路由
|
||||
learningRouter.InitChapterRouter(privateGroup, publicGroup) // 章节路由
|
||||
learningRouter.InitKnowledgePointRouter(privateGroup, publicGroup) // 知识点路由
|
||||
learningRouter.InitQuestionRouter(privateGroup, publicGroup) // 题目路由
|
||||
learningRouter.InitUserLearningRouter(privateGroup, publicGroup) // 用户学习记录路由
|
||||
learningRouter.InitWrongQuestionRouter(privateGroup, publicGroup) // 错题本路由
|
||||
learningRouter.InitExamRouter(privateGroup, publicGroup) // 考试路由
|
||||
learningRouter.InitUserExamRouter(privateGroup, publicGroup) // 用户考试记录路由
|
||||
}
|
||||
|
||||
holder(publicGroup, privateGroup)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package learning
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
)
|
||||
|
||||
// Chapter 章节模型
|
||||
type Chapter struct {
|
||||
global.GVA_MODEL
|
||||
CourseId uint `json:"courseId" form:"courseId" gorm:"column:course_id;comment:关联课程ID;not null;" binding:"required"`
|
||||
Title string `json:"title" form:"title" gorm:"column:title;comment:章节标题;size:255;not null;" binding:"required"`
|
||||
Description string `json:"description" form:"description" gorm:"column:description;comment:章节描述;type:text;"`
|
||||
Sort int `json:"sort" form:"sort" gorm:"column:sort;comment:排序号;default:0;"`
|
||||
Status string `json:"status" form:"status" gorm:"column:status;comment:章节状态;size:50;default:draft;"`
|
||||
Duration int `json:"duration" form:"duration" gorm:"column:duration;comment:预计学习时长(分钟);default:0;"`
|
||||
|
||||
// 关联关系 - 去掉外键约束,只保留逻辑关联
|
||||
Course Course `json:"course" gorm:"-"`
|
||||
}
|
||||
|
||||
// TableName 设置表名
|
||||
func (Chapter) TableName() string {
|
||||
return "learning_chapters"
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package learning
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
)
|
||||
|
||||
// Course 课程模型
|
||||
type Course struct {
|
||||
global.GVA_MODEL
|
||||
Title string `json:"title" form:"title" gorm:"column:title;comment:课程标题;size:255;not null;" binding:"required"`
|
||||
Description string `json:"description" form:"description" gorm:"column:description;comment:课程描述;type:longtext;"`
|
||||
Cover string `json:"cover" form:"cover" gorm:"column:cover;comment:课程封面图片URL;size:500;"`
|
||||
Difficulty string `json:"difficulty" form:"difficulty" gorm:"column:difficulty;comment:难度等级;size:50;default:beginner;"`
|
||||
Duration int `json:"duration" form:"duration" gorm:"column:duration;comment:预计学时(分钟);default:0;"`
|
||||
Status string `json:"status" form:"status" gorm:"column:status;comment:课程状态;size:50;default:draft;"`
|
||||
Price float64 `json:"price" form:"price" gorm:"column:price;comment:课程价格;type:decimal(10,2);default:0.00;"`
|
||||
Tags string `json:"tags" form:"tags" gorm:"column:tags;comment:课程标签,逗号分隔;size:500;"`
|
||||
Sort int `json:"sort" form:"sort" gorm:"column:sort;comment:排序权重;default:0;"`
|
||||
}
|
||||
|
||||
// TableName 设置表名
|
||||
func (Course) TableName() string {
|
||||
return "learning_courses"
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package learning
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
)
|
||||
|
||||
// Exam 考试模型
|
||||
type Exam struct {
|
||||
global.GVA_MODEL
|
||||
Title string `json:"title" form:"title" gorm:"column:title;comment:考试标题;size:255;not null;" binding:"required"`
|
||||
Description string `json:"description" form:"description" gorm:"column:description;comment:考试描述;type:longtext;"`
|
||||
CourseId uint `json:"courseId" form:"courseId" gorm:"column:course_id;comment:关联课程ID;"`
|
||||
Duration int `json:"duration" form:"duration" gorm:"column:duration;comment:考试时长(分钟);not null;" binding:"required"`
|
||||
TotalScore int `json:"totalScore" form:"totalScore" gorm:"column:total_score;comment:总分;not null;" binding:"required"`
|
||||
PassScore int `json:"passScore" form:"passScore" gorm:"column:pass_score;comment:及格分数;not null;" binding:"required"`
|
||||
QuestionCount int `json:"questionCount" form:"questionCount" gorm:"column:question_count;comment:题目数量;not null;" binding:"required"`
|
||||
StartTime time.Time `json:"startTime" form:"startTime" gorm:"column:start_time;comment:开始时间;"`
|
||||
EndTime time.Time `json:"endTime" form:"endTime" gorm:"column:end_time;comment:结束时间;"`
|
||||
Status string `json:"status" form:"status" gorm:"column:status;comment:考试状态;size:50;default:draft;"`
|
||||
AllowRetake bool `json:"allowRetake" form:"allowRetake" gorm:"column:allow_retake;comment:是否允许重考;default:false;"`
|
||||
MaxAttempts int `json:"maxAttempts" form:"maxAttempts" gorm:"column:max_attempts;comment:最大尝试次数;default:1;"`
|
||||
|
||||
// 关联关系 - 去掉外键约束,只保留逻辑关联
|
||||
Course Course `json:"course" gorm:"-"`
|
||||
}
|
||||
|
||||
// TableName 设置表名
|
||||
func (Exam) TableName() string {
|
||||
return "learning_exams"
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package learning
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
)
|
||||
|
||||
// KnowledgePoint 知识点模型
|
||||
type KnowledgePoint struct {
|
||||
global.GVA_MODEL
|
||||
ChapterId uint `json:"chapterId" form:"chapterId" gorm:"column:chapter_id;comment:关联章节ID;not null;" binding:"required"`
|
||||
Title string `json:"title" form:"title" gorm:"column:title;comment:知识点标题;size:255;not null;" binding:"required"`
|
||||
Content string `json:"content" form:"content" gorm:"column:content;comment:知识点内容;type:longtext;"`
|
||||
VideoUrl string `json:"videoUrl" form:"videoUrl" gorm:"column:video_url;comment:视频链接;size:500;"`
|
||||
Attachments string `json:"attachments" form:"attachments" gorm:"column:attachments;comment:附件信息JSON;type:text;"`
|
||||
Sort int `json:"sort" form:"sort" gorm:"column:sort;comment:排序号;default:0;"`
|
||||
Duration int `json:"duration" form:"duration" gorm:"column:duration;comment:预计学习时长(分钟);default:0;"`
|
||||
Status string `json:"status" form:"status" gorm:"column:status;comment:知识点状态;size:50;default:draft;"`
|
||||
|
||||
// 关联关系 - 去掉外键约束,只保留逻辑关联
|
||||
Chapter Chapter `json:"chapter" gorm:"-"`
|
||||
}
|
||||
|
||||
// TableName 设置表名
|
||||
func (KnowledgePoint) TableName() string {
|
||||
return "learning_knowledge_points"
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package learning
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
)
|
||||
|
||||
// Question 题目模型
|
||||
type Question struct {
|
||||
global.GVA_MODEL
|
||||
KnowledgePointId uint `json:"knowledgePointId" form:"knowledgePointId" gorm:"column:knowledge_point_id;comment:关联知识点ID;not null;" binding:"required"`
|
||||
Title string `json:"title" form:"title" gorm:"column:title;comment:题目标题;size:255;not null;" binding:"required"`
|
||||
Content string `json:"content" form:"content" gorm:"column:content;comment:题目内容;type:longtext;"`
|
||||
Type string `json:"type" form:"type" gorm:"column:type;comment:题目类型;size:50;not null;" binding:"required"`
|
||||
Options string `json:"options" form:"options" gorm:"column:options;comment:选项内容JSON;type:text;"`
|
||||
Answer string `json:"answer" form:"answer" gorm:"column:answer;comment:正确答案;type:text;not null;" binding:"required"`
|
||||
Explanation string `json:"explanation" form:"explanation" gorm:"column:explanation;comment:答案解析;type:longtext;"`
|
||||
Difficulty string `json:"difficulty" form:"difficulty" gorm:"column:difficulty;comment:难度等级;size:50;default:medium;"`
|
||||
Score int `json:"score" form:"score" gorm:"column:score;comment:题目分值;default:5;"`
|
||||
|
||||
// 关联关系 - 去掉外键约束,只保留逻辑关联
|
||||
KnowledgePoint KnowledgePoint `json:"knowledgePoint" gorm:"-"`
|
||||
}
|
||||
|
||||
// TableName 设置表名
|
||||
func (Question) TableName() string {
|
||||
return "learning_questions"
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package request
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/learning"
|
||||
)
|
||||
|
||||
type ChapterSearch struct {
|
||||
learning.Chapter
|
||||
request.PageInfo
|
||||
StartCreatedAt *string `json:"startCreatedAt" form:"startCreatedAt"`
|
||||
EndCreatedAt *string `json:"endCreatedAt" form:"endCreatedAt"`
|
||||
}
|
||||
|
||||
type ChapterCreate struct {
|
||||
CourseId uint `json:"courseId" binding:"required"`
|
||||
Title string `json:"title" binding:"required"`
|
||||
Description string `json:"description"`
|
||||
Sort int `json:"sort"`
|
||||
Status string `json:"status"`
|
||||
Duration int `json:"duration"`
|
||||
}
|
||||
|
||||
type ChapterUpdate struct {
|
||||
ID uint `json:"id" binding:"required"`
|
||||
CourseId uint `json:"courseId" binding:"required"`
|
||||
Title string `json:"title" binding:"required"`
|
||||
Description string `json:"description"`
|
||||
Sort int `json:"sort"`
|
||||
Status string `json:"status"`
|
||||
Duration int `json:"duration"`
|
||||
}
|
||||
|
||||
type ChaptersByCourseRequest struct {
|
||||
CourseId uint `json:"courseId" form:"courseId" binding:"required"`
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package request
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/learning"
|
||||
)
|
||||
|
||||
type CourseSearch struct {
|
||||
learning.Course
|
||||
request.PageInfo
|
||||
StartCreatedAt *string `json:"startCreatedAt" form:"startCreatedAt"`
|
||||
EndCreatedAt *string `json:"endCreatedAt" form:"endCreatedAt"`
|
||||
}
|
||||
|
||||
type CourseCreate struct {
|
||||
Title string `json:"title" binding:"required"`
|
||||
Description string `json:"description"`
|
||||
Cover string `json:"cover"`
|
||||
Difficulty string `json:"difficulty"`
|
||||
Duration int `json:"duration"`
|
||||
Status string `json:"status"`
|
||||
Price float64 `json:"price"`
|
||||
Tags string `json:"tags"`
|
||||
Sort int `json:"sort"`
|
||||
}
|
||||
|
||||
type CourseUpdate struct {
|
||||
ID uint `json:"id" binding:"required"`
|
||||
Title string `json:"title" binding:"required"`
|
||||
Description string `json:"description"`
|
||||
Cover string `json:"cover"`
|
||||
Difficulty string `json:"difficulty"`
|
||||
Duration int `json:"duration"`
|
||||
Status string `json:"status"`
|
||||
Price float64 `json:"price"`
|
||||
Tags string `json:"tags"`
|
||||
Sort int `json:"sort"`
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package request
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/learning"
|
||||
)
|
||||
|
||||
type ExamSearch struct {
|
||||
learning.Exam
|
||||
request.PageInfo
|
||||
StartCreatedAt *string `json:"startCreatedAt" form:"startCreatedAt"`
|
||||
EndCreatedAt *string `json:"endCreatedAt" form:"endCreatedAt"`
|
||||
}
|
||||
|
||||
type ExamCreate struct {
|
||||
Title string `json:"title" binding:"required"`
|
||||
Description string `json:"description"`
|
||||
CourseId uint `json:"courseId"`
|
||||
Duration int `json:"duration" binding:"required"`
|
||||
TotalScore int `json:"totalScore" binding:"required"`
|
||||
PassScore int `json:"passScore" binding:"required"`
|
||||
QuestionCount int `json:"questionCount" binding:"required"`
|
||||
StartTime time.Time `json:"startTime"`
|
||||
EndTime time.Time `json:"endTime"`
|
||||
Status string `json:"status"`
|
||||
AllowRetake bool `json:"allowRetake"`
|
||||
MaxAttempts int `json:"maxAttempts"`
|
||||
}
|
||||
|
||||
type ExamUpdate struct {
|
||||
ID uint `json:"id" binding:"required"`
|
||||
Title string `json:"title" binding:"required"`
|
||||
Description string `json:"description"`
|
||||
CourseId uint `json:"courseId"`
|
||||
Duration int `json:"duration" binding:"required"`
|
||||
TotalScore int `json:"totalScore" binding:"required"`
|
||||
PassScore int `json:"passScore" binding:"required"`
|
||||
QuestionCount int `json:"questionCount" binding:"required"`
|
||||
StartTime time.Time `json:"startTime"`
|
||||
EndTime time.Time `json:"endTime"`
|
||||
Status string `json:"status"`
|
||||
AllowRetake bool `json:"allowRetake"`
|
||||
MaxAttempts int `json:"maxAttempts"`
|
||||
}
|
||||
|
||||
type ExamStatistics struct {
|
||||
TotalExams int `json:"totalExams"` // 总考试数
|
||||
ActiveExams int `json:"activeExams"` // 进行中的考试数
|
||||
CompletedExams int `json:"completedExams"` // 已结束的考试数
|
||||
DraftExams int `json:"draftExams"` // 草稿考试数
|
||||
}
|
||||
|
||||
type PublishExamRequest struct {
|
||||
ID uint `json:"id" binding:"required"`
|
||||
}
|
||||
|
||||
type ExamQuestionConfig struct {
|
||||
ExamId uint `json:"examId" binding:"required"`
|
||||
QuestionId uint `json:"questionId" binding:"required"`
|
||||
Score int `json:"score" binding:"required"`
|
||||
QuestionOrder int `json:"questionOrder"`
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package request
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/learning"
|
||||
)
|
||||
|
||||
type KnowledgePointSearch struct {
|
||||
learning.KnowledgePoint
|
||||
request.PageInfo
|
||||
StartCreatedAt *string `json:"startCreatedAt" form:"startCreatedAt"`
|
||||
EndCreatedAt *string `json:"endCreatedAt" form:"endCreatedAt"`
|
||||
}
|
||||
|
||||
type KnowledgePointCreate struct {
|
||||
ChapterId uint `json:"chapterId" binding:"required"`
|
||||
Title string `json:"title" binding:"required"`
|
||||
Content string `json:"content"`
|
||||
VideoUrl string `json:"videoUrl"`
|
||||
Attachments string `json:"attachments"`
|
||||
Sort int `json:"sort"`
|
||||
Duration int `json:"duration"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
type KnowledgePointUpdate struct {
|
||||
ID uint `json:"id" binding:"required"`
|
||||
ChapterId uint `json:"chapterId" binding:"required"`
|
||||
Title string `json:"title" binding:"required"`
|
||||
Content string `json:"content"`
|
||||
VideoUrl string `json:"videoUrl"`
|
||||
Attachments string `json:"attachments"`
|
||||
Sort int `json:"sort"`
|
||||
Duration int `json:"duration"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
type KnowledgePointsByChapterRequest struct {
|
||||
ChapterId uint `json:"chapterId" form:"chapterId" binding:"required"`
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package request
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/learning"
|
||||
)
|
||||
|
||||
type QuestionSearch struct {
|
||||
learning.Question
|
||||
request.PageInfo
|
||||
StartCreatedAt *string `json:"startCreatedAt" form:"startCreatedAt"`
|
||||
EndCreatedAt *string `json:"endCreatedAt" form:"endCreatedAt"`
|
||||
}
|
||||
|
||||
type QuestionCreate struct {
|
||||
KnowledgePointId uint `json:"knowledgePointId" binding:"required"`
|
||||
Title string `json:"title" binding:"required"`
|
||||
Content string `json:"content"`
|
||||
Type string `json:"type" binding:"required"`
|
||||
Options string `json:"options"`
|
||||
Answer string `json:"answer" binding:"required"`
|
||||
Explanation string `json:"explanation"`
|
||||
Difficulty string `json:"difficulty"`
|
||||
Score int `json:"score"`
|
||||
}
|
||||
|
||||
type QuestionUpdate struct {
|
||||
ID uint `json:"id" binding:"required"`
|
||||
KnowledgePointId uint `json:"knowledgePointId" binding:"required"`
|
||||
Title string `json:"title" binding:"required"`
|
||||
Content string `json:"content"`
|
||||
Type string `json:"type" binding:"required"`
|
||||
Options string `json:"options"`
|
||||
Answer string `json:"answer" binding:"required"`
|
||||
Explanation string `json:"explanation"`
|
||||
Difficulty string `json:"difficulty"`
|
||||
Score int `json:"score"`
|
||||
}
|
||||
|
||||
type QuestionsByKnowledgePointRequest struct {
|
||||
KnowledgePointId uint `json:"knowledgePointId" form:"knowledgePointId" binding:"required"`
|
||||
}
|
||||
|
||||
// QuestionOption 题目选项结构
|
||||
type QuestionOption struct {
|
||||
Key string `json:"key"` // 选项标识 A、B、C、D
|
||||
Value string `json:"value"` // 选项内容
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package request
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/learning"
|
||||
)
|
||||
|
||||
type UserExamSearch struct {
|
||||
learning.UserExam
|
||||
request.PageInfo
|
||||
StartCreatedAt *string `json:"startCreatedAt" form:"startCreatedAt"`
|
||||
EndCreatedAt *string `json:"endCreatedAt" form:"endCreatedAt"`
|
||||
}
|
||||
|
||||
type UserExamCreate struct {
|
||||
UserId uint `json:"userId" binding:"required"`
|
||||
ExamId uint `json:"examId" binding:"required"`
|
||||
AttemptNumber int `json:"attemptNumber"`
|
||||
StartTime time.Time `json:"startTime"`
|
||||
EndTime time.Time `json:"endTime"`
|
||||
Duration int `json:"duration"`
|
||||
TotalScore int `json:"totalScore"`
|
||||
CorrectCount int `json:"correctCount"`
|
||||
WrongCount int `json:"wrongCount"`
|
||||
Status string `json:"status"`
|
||||
IsPassed bool `json:"isPassed"`
|
||||
SubmittedAt time.Time `json:"submittedAt"`
|
||||
}
|
||||
|
||||
type UserExamUpdate struct {
|
||||
ID uint `json:"id" binding:"required"`
|
||||
UserId uint `json:"userId" binding:"required"`
|
||||
ExamId uint `json:"examId" binding:"required"`
|
||||
AttemptNumber int `json:"attemptNumber"`
|
||||
StartTime time.Time `json:"startTime"`
|
||||
EndTime time.Time `json:"endTime"`
|
||||
Duration int `json:"duration"`
|
||||
TotalScore int `json:"totalScore"`
|
||||
CorrectCount int `json:"correctCount"`
|
||||
WrongCount int `json:"wrongCount"`
|
||||
Status string `json:"status"`
|
||||
IsPassed bool `json:"isPassed"`
|
||||
SubmittedAt time.Time `json:"submittedAt"`
|
||||
}
|
||||
|
||||
type StartExamRequest struct {
|
||||
ExamId uint `json:"examId" binding:"required"`
|
||||
UserId uint `json:"userId" binding:"required"`
|
||||
}
|
||||
|
||||
type SubmitExamRequest struct {
|
||||
UserExamId uint `json:"userExamId" binding:"required"`
|
||||
TotalScore int `json:"totalScore"`
|
||||
CorrectCount int `json:"correctCount"`
|
||||
WrongCount int `json:"wrongCount"`
|
||||
}
|
||||
|
||||
type UserExamStatistics struct {
|
||||
TotalExams int `json:"totalExams"` // 总考试次数
|
||||
PassedExams int `json:"passedExams"` // 通过考试次数
|
||||
FailedExams int `json:"failedExams"` // 未通过考试次数
|
||||
AverageScore float64 `json:"averageScore"` // 平均分数
|
||||
HighestScore int `json:"highestScore"` // 最高分数
|
||||
TotalStudyTime int `json:"totalStudyTime"` // 总学习时间(分钟)
|
||||
PassRate float64 `json:"passRate"` // 通过率
|
||||
}
|
||||
|
||||
type ExamRankingItem struct {
|
||||
UserId uint `json:"userId"`
|
||||
UserName string `json:"userName"`
|
||||
Score int `json:"score"`
|
||||
Duration int `json:"duration"`
|
||||
Rank int `json:"rank"`
|
||||
IsPassed bool `json:"isPassed"`
|
||||
SubmittedAt time.Time `json:"submittedAt"`
|
||||
}
|
||||
|
||||
type ExamRankingResponse struct {
|
||||
ExamId uint `json:"examId"`
|
||||
ExamTitle string `json:"examTitle"`
|
||||
Rankings []ExamRankingItem `json:"rankings"`
|
||||
Total int64 `json:"total"`
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package request
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/learning"
|
||||
)
|
||||
|
||||
type UserLearningSearch struct {
|
||||
learning.UserLearning
|
||||
request.PageInfo
|
||||
StartCreatedAt *string `json:"startCreatedAt" form:"startCreatedAt"`
|
||||
EndCreatedAt *string `json:"endCreatedAt" form:"endCreatedAt"`
|
||||
}
|
||||
|
||||
type UserLearningCreate struct {
|
||||
UserId uint `json:"userId" binding:"required"`
|
||||
CourseId uint `json:"courseId" binding:"required"`
|
||||
ChapterId uint `json:"chapterId"`
|
||||
KnowledgePointId uint `json:"knowledgePointId"`
|
||||
Progress int `json:"progress"`
|
||||
StudyTime int `json:"studyTime"`
|
||||
LastStudyAt time.Time `json:"lastStudyAt"`
|
||||
Status string `json:"status"`
|
||||
Score int `json:"score"`
|
||||
CompletedAt time.Time `json:"completedAt"`
|
||||
}
|
||||
|
||||
type UserLearningUpdate struct {
|
||||
ID uint `json:"id" binding:"required"`
|
||||
UserId uint `json:"userId" binding:"required"`
|
||||
CourseId uint `json:"courseId" binding:"required"`
|
||||
ChapterId uint `json:"chapterId"`
|
||||
KnowledgePointId uint `json:"knowledgePointId"`
|
||||
Progress int `json:"progress"`
|
||||
StudyTime int `json:"studyTime"`
|
||||
LastStudyAt time.Time `json:"lastStudyAt"`
|
||||
Status string `json:"status"`
|
||||
Score int `json:"score"`
|
||||
CompletedAt time.Time `json:"completedAt"`
|
||||
}
|
||||
|
||||
type UserProgressRequest struct {
|
||||
UserId uint `json:"userId" form:"userId" binding:"required"`
|
||||
CourseId uint `json:"courseId" form:"courseId"`
|
||||
}
|
||||
|
||||
type LearningStatistics struct {
|
||||
TotalCourses int `json:"totalCourses"` // 总课程数
|
||||
CompletedCourses int `json:"completedCourses"` // 已完成课程数
|
||||
InProgressCourses int `json:"inProgressCourses"` // 学习中课程数
|
||||
TotalStudyTime int `json:"totalStudyTime"` // 总学习时长
|
||||
AverageScore int `json:"averageScore"` // 平均分数
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package request
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/learning"
|
||||
)
|
||||
|
||||
type WrongQuestionSearch struct {
|
||||
learning.WrongQuestion
|
||||
request.PageInfo
|
||||
StartCreatedAt *string `json:"startCreatedAt" form:"startCreatedAt"`
|
||||
EndCreatedAt *string `json:"endCreatedAt" form:"endCreatedAt"`
|
||||
}
|
||||
|
||||
type WrongQuestionCreate struct {
|
||||
UserId uint `json:"userId" binding:"required"`
|
||||
QuestionId uint `json:"questionId" binding:"required"`
|
||||
WrongAnswer string `json:"wrongAnswer"`
|
||||
WrongCount int `json:"wrongCount"`
|
||||
LastWrongAt time.Time `json:"lastWrongAt"`
|
||||
IsMastered bool `json:"isMastered"`
|
||||
MasteredAt time.Time `json:"masteredAt"`
|
||||
Notes string `json:"notes"`
|
||||
}
|
||||
|
||||
type WrongQuestionUpdate struct {
|
||||
ID uint `json:"id" binding:"required"`
|
||||
UserId uint `json:"userId" binding:"required"`
|
||||
QuestionId uint `json:"questionId" binding:"required"`
|
||||
WrongAnswer string `json:"wrongAnswer"`
|
||||
WrongCount int `json:"wrongCount"`
|
||||
LastWrongAt time.Time `json:"lastWrongAt"`
|
||||
IsMastered bool `json:"isMastered"`
|
||||
MasteredAt time.Time `json:"masteredAt"`
|
||||
Notes string `json:"notes"`
|
||||
}
|
||||
|
||||
type WrongQuestionStatistics struct {
|
||||
TotalWrongQuestions int `json:"totalWrongQuestions"` // 总错题数
|
||||
MasteredQuestions int `json:"masteredQuestions"` // 已掌握题目数
|
||||
UnmasteredQuestions int `json:"unmasteredQuestions"` // 未掌握题目数
|
||||
AverageWrongCount int `json:"averageWrongCount"` // 平均错误次数
|
||||
}
|
||||
|
||||
type MarkMasteredRequest struct {
|
||||
ID uint `json:"id" binding:"required"`
|
||||
UserId uint `json:"userId" binding:"required"`
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package learning
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
)
|
||||
|
||||
// UserExam 用户考试记录模型
|
||||
type UserExam struct {
|
||||
global.GVA_MODEL
|
||||
UserId uint `json:"userId" form:"userId" gorm:"column:user_id;comment:关联用户ID;not null;" binding:"required"`
|
||||
ExamId uint `json:"examId" form:"examId" gorm:"column:exam_id;comment:关联考试ID;not null;" binding:"required"`
|
||||
AttemptNumber int `json:"attemptNumber" form:"attemptNumber" gorm:"column:attempt_number;comment:尝试次数;default:1;"`
|
||||
StartTime time.Time `json:"startTime" form:"startTime" gorm:"column:start_time;comment:开始时间;"`
|
||||
EndTime time.Time `json:"endTime" form:"endTime" gorm:"column:end_time;comment:结束时间;"`
|
||||
Duration int `json:"duration" form:"duration" gorm:"column:duration;comment:实际用时(分钟);"`
|
||||
TotalScore int `json:"totalScore" form:"totalScore" gorm:"column:total_score;comment:总得分;"`
|
||||
CorrectCount int `json:"correctCount" form:"correctCount" gorm:"column:correct_count;comment:正确题目数;"`
|
||||
WrongCount int `json:"wrongCount" form:"wrongCount" gorm:"column:wrong_count;comment:错误题目数;"`
|
||||
Status string `json:"status" form:"status" gorm:"column:status;comment:状态;size:50;default:in_progress;"`
|
||||
IsPassed bool `json:"isPassed" form:"isPassed" gorm:"column:is_passed;comment:是否通过;default:false;"`
|
||||
SubmittedAt time.Time `json:"submittedAt" form:"submittedAt" gorm:"column:submitted_at;comment:提交时间;"`
|
||||
|
||||
// 关联关系 - 去掉外键约束,只保留逻辑关联
|
||||
Exam Exam `json:"exam" gorm:"-"`
|
||||
}
|
||||
|
||||
// TableName 设置表名
|
||||
func (UserExam) TableName() string {
|
||||
return "learning_user_exams"
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package learning
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
)
|
||||
|
||||
// UserLearning 用户学习记录模型
|
||||
type UserLearning struct {
|
||||
global.GVA_MODEL
|
||||
UserId uint `json:"userId" form:"userId" gorm:"column:user_id;comment:关联用户ID;not null;" binding:"required"`
|
||||
CourseId uint `json:"courseId" form:"courseId" gorm:"column:course_id;comment:关联课程ID;not null;" binding:"required"`
|
||||
ChapterId uint `json:"chapterId" form:"chapterId" gorm:"column:chapter_id;comment:当前学习章节ID;"`
|
||||
KnowledgePointId uint `json:"knowledgePointId" form:"knowledgePointId" gorm:"column:knowledge_point_id;comment:当前学习知识点ID;"`
|
||||
Progress int `json:"progress" form:"progress" gorm:"column:progress;comment:学习进度百分比;default:0;"`
|
||||
StudyTime int `json:"studyTime" form:"studyTime" gorm:"column:study_time;comment:累计学习时长(分钟);default:0;"`
|
||||
LastStudyAt time.Time `json:"lastStudyAt" form:"lastStudyAt" gorm:"column:last_study_at;comment:最后学习时间;"`
|
||||
Status string `json:"status" form:"status" gorm:"column:status;comment:学习状态;size:50;default:not_started;"`
|
||||
Score int `json:"score" form:"score" gorm:"column:score;comment:总分数;default:0;"`
|
||||
CompletedAt time.Time `json:"completedAt" form:"completedAt" gorm:"column:completed_at;comment:完成时间;"`
|
||||
|
||||
// 关联关系 - 去掉外键约束,只保留逻辑关联
|
||||
Course Course `json:"course" gorm:"-"`
|
||||
Chapter Chapter `json:"chapter" gorm:"-"`
|
||||
KnowledgePoint KnowledgePoint `json:"knowledgePoint" gorm:"-"`
|
||||
}
|
||||
|
||||
// TableName 设置表名
|
||||
func (UserLearning) TableName() string {
|
||||
return "learning_user_learning"
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package learning
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
)
|
||||
|
||||
// WrongQuestion 错题本模型
|
||||
type WrongQuestion struct {
|
||||
global.GVA_MODEL
|
||||
UserId uint `json:"userId" form:"userId" gorm:"column:user_id;comment:关联用户ID;not null;" binding:"required"`
|
||||
QuestionId uint `json:"questionId" form:"questionId" gorm:"column:question_id;comment:关联题目ID;not null;" binding:"required"`
|
||||
WrongAnswer string `json:"wrongAnswer" form:"wrongAnswer" gorm:"column:wrong_answer;comment:错误答案;type:text;"`
|
||||
WrongCount int `json:"wrongCount" form:"wrongCount" gorm:"column:wrong_count;comment:错误次数;default:1;"`
|
||||
LastWrongAt time.Time `json:"lastWrongAt" form:"lastWrongAt" gorm:"column:last_wrong_at;comment:最后错误时间;"`
|
||||
IsMastered bool `json:"isMastered" form:"isMastered" gorm:"column:is_mastered;comment:是否已掌握;default:false;"`
|
||||
MasteredAt time.Time `json:"masteredAt" form:"masteredAt" gorm:"column:mastered_at;comment:掌握时间;"`
|
||||
Notes string `json:"notes" form:"notes" gorm:"column:notes;comment:用户笔记;type:longtext;"`
|
||||
|
||||
// 关联关系 - 去掉外键约束,只保留逻辑关联
|
||||
Question Question `json:"question" gorm:"-"`
|
||||
}
|
||||
|
||||
// TableName 设置表名
|
||||
func (WrongQuestion) TableName() string {
|
||||
return "learning_wrong_questions"
|
||||
}
|
||||
@@ -2,12 +2,14 @@ package router
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/router/example"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/router/learning"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/router/system"
|
||||
)
|
||||
|
||||
var RouterGroupApp = new(RouterGroup)
|
||||
|
||||
type RouterGroup struct {
|
||||
System system.RouterGroup
|
||||
Example example.RouterGroup
|
||||
System system.RouterGroup
|
||||
Example example.RouterGroup
|
||||
Learning learning.RouterGroup
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package learning
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/api/v1"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/middleware"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type ChapterRouter struct{}
|
||||
|
||||
// InitChapterRouter 初始化 章节 路由信息
|
||||
func (s *ChapterRouter) InitChapterRouter(Router *gin.RouterGroup, PublicRouter *gin.RouterGroup) {
|
||||
chapterRouter := Router.Group("chapter").Use(middleware.OperationRecord())
|
||||
chapterRouterWithoutRecord := Router.Group("chapter")
|
||||
chapterRouterWithoutAuth := PublicRouter.Group("chapter")
|
||||
|
||||
var chapterApi = v1.ApiGroupApp.LearningApiGroup.ChapterApi
|
||||
{
|
||||
chapterRouter.POST("createChapter", chapterApi.CreateChapter) // 新建章节
|
||||
chapterRouter.DELETE("deleteChapter", chapterApi.DeleteChapter) // 删除章节
|
||||
chapterRouter.DELETE("deleteChapterByIds", chapterApi.DeleteChapterByIds) // 批量删除章节
|
||||
chapterRouter.PUT("updateChapter", chapterApi.UpdateChapter) // 更新章节
|
||||
}
|
||||
{
|
||||
chapterRouterWithoutRecord.GET("findChapter", chapterApi.FindChapter) // 根据ID获取章节
|
||||
chapterRouterWithoutRecord.GET("getChapterList", chapterApi.GetChapterList) // 获取章节列表
|
||||
}
|
||||
{
|
||||
chapterRouterWithoutAuth.GET("getChaptersByCourse", chapterApi.GetChaptersByCourse) // 根据课程获取章节列表(无需认证)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package learning
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/api/v1"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/middleware"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type CourseRouter struct{}
|
||||
|
||||
// InitCourseRouter 初始化 课程 路由信息
|
||||
func (s *CourseRouter) InitCourseRouter(Router *gin.RouterGroup, PublicRouter *gin.RouterGroup) {
|
||||
courseRouter := Router.Group("course").Use(middleware.OperationRecord())
|
||||
courseRouterWithoutRecord := Router.Group("course")
|
||||
courseRouterWithoutAuth := PublicRouter.Group("course")
|
||||
|
||||
var courseApi = v1.ApiGroupApp.LearningApiGroup.CourseApi
|
||||
{
|
||||
courseRouter.POST("createCourse", courseApi.CreateCourse) // 新建课程
|
||||
courseRouter.DELETE("deleteCourse", courseApi.DeleteCourse) // 删除课程
|
||||
courseRouter.DELETE("deleteCourseByIds", courseApi.DeleteCourseByIds) // 批量删除课程
|
||||
courseRouter.PUT("updateCourse", courseApi.UpdateCourse) // 更新课程
|
||||
}
|
||||
{
|
||||
courseRouterWithoutRecord.GET("findCourse", courseApi.FindCourse) // 根据ID获取课程
|
||||
courseRouterWithoutRecord.GET("getCourseList", courseApi.GetCourseList) // 获取课程列表
|
||||
}
|
||||
{
|
||||
courseRouterWithoutAuth.GET("getPublishedCourses", courseApi.GetPublishedCourses) // 获取已发布课程列表(无需认证)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package learning
|
||||
|
||||
type RouterGroup struct {
|
||||
CourseRouter
|
||||
ChapterRouter
|
||||
KnowledgePointRouter
|
||||
QuestionRouter
|
||||
UserLearningRouter
|
||||
WrongQuestionRouter
|
||||
ExamRouter
|
||||
UserExamRouter
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package learning
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/api/v1"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/middleware"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type ExamRouter struct{}
|
||||
|
||||
// InitExamRouter 初始化 考试 路由信息
|
||||
func (s *ExamRouter) InitExamRouter(Router *gin.RouterGroup, PublicRouter *gin.RouterGroup) {
|
||||
examRouter := Router.Group("exam").Use(middleware.OperationRecord())
|
||||
examRouterWithoutRecord := Router.Group("exam")
|
||||
examRouterWithoutAuth := PublicRouter.Group("exam")
|
||||
|
||||
var examApi = v1.ApiGroupApp.LearningApiGroup.ExamApi
|
||||
{
|
||||
examRouter.POST("createExam", examApi.CreateExam) // 新建考试
|
||||
examRouter.DELETE("deleteExam", examApi.DeleteExam) // 删除考试
|
||||
examRouter.DELETE("deleteExamByIds", examApi.DeleteExamByIds) // 批量删除考试
|
||||
examRouter.PUT("updateExam", examApi.UpdateExam) // 更新考试
|
||||
examRouter.PUT("publishExam", examApi.PublishExam) // 发布考试
|
||||
}
|
||||
{
|
||||
examRouterWithoutRecord.GET("findExam", examApi.FindExam) // 根据ID获取考试
|
||||
examRouterWithoutRecord.GET("getExamList", examApi.GetExamList) // 获取考试列表
|
||||
examRouterWithoutRecord.GET("getActiveExams", examApi.GetActiveExams) // 获取进行中的考试列表
|
||||
examRouterWithoutRecord.GET("getExamsByCourse", examApi.GetExamsByCourse) // 根据课程获取考试列表
|
||||
examRouterWithoutRecord.GET("getExamStatistics", examApi.GetExamStatistics) // 获取考试统计
|
||||
}
|
||||
{
|
||||
// 公开接口,不需要认证
|
||||
examRouterWithoutAuth.GET("getPublishedExams", examApi.GetActiveExams) // 获取已发布的考试列表
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package learning
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/api/v1"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/middleware"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type KnowledgePointRouter struct{}
|
||||
|
||||
// InitKnowledgePointRouter 初始化 知识点 路由信息
|
||||
func (s *KnowledgePointRouter) InitKnowledgePointRouter(Router *gin.RouterGroup, PublicRouter *gin.RouterGroup) {
|
||||
knowledgePointRouter := Router.Group("knowledgePoint").Use(middleware.OperationRecord())
|
||||
knowledgePointRouterWithoutRecord := Router.Group("knowledgePoint")
|
||||
knowledgePointRouterWithoutAuth := PublicRouter.Group("knowledgePoint")
|
||||
|
||||
var knowledgePointApi = v1.ApiGroupApp.LearningApiGroup.KnowledgePointApi
|
||||
{
|
||||
knowledgePointRouter.POST("createKnowledgePoint", knowledgePointApi.CreateKnowledgePoint) // 新建知识点
|
||||
knowledgePointRouter.DELETE("deleteKnowledgePoint", knowledgePointApi.DeleteKnowledgePoint) // 删除知识点
|
||||
knowledgePointRouter.DELETE("deleteKnowledgePointByIds", knowledgePointApi.DeleteKnowledgePointByIds) // 批量删除知识点
|
||||
knowledgePointRouter.PUT("updateKnowledgePoint", knowledgePointApi.UpdateKnowledgePoint) // 更新知识点
|
||||
}
|
||||
{
|
||||
knowledgePointRouterWithoutRecord.GET("findKnowledgePoint", knowledgePointApi.FindKnowledgePoint) // 根据ID获取知识点
|
||||
knowledgePointRouterWithoutRecord.GET("getKnowledgePointList", knowledgePointApi.GetKnowledgePointList) // 获取知识点列表
|
||||
}
|
||||
{
|
||||
knowledgePointRouterWithoutAuth.GET("getKnowledgePointsByChapter", knowledgePointApi.GetKnowledgePointsByChapter) // 根据章节获取知识点列表(无需认证)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package learning
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/api/v1"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/middleware"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type QuestionRouter struct{}
|
||||
|
||||
// InitQuestionRouter 初始化 题目 路由信息
|
||||
func (s *QuestionRouter) InitQuestionRouter(Router *gin.RouterGroup, PublicRouter *gin.RouterGroup) {
|
||||
questionRouter := Router.Group("question").Use(middleware.OperationRecord())
|
||||
questionRouterWithoutRecord := Router.Group("question")
|
||||
questionRouterWithoutAuth := PublicRouter.Group("question")
|
||||
|
||||
var questionApi = v1.ApiGroupApp.LearningApiGroup.QuestionApi
|
||||
{
|
||||
questionRouter.POST("createQuestion", questionApi.CreateQuestion) // 新建题目
|
||||
questionRouter.DELETE("deleteQuestion", questionApi.DeleteQuestion) // 删除题目
|
||||
questionRouter.DELETE("deleteQuestionByIds", questionApi.DeleteQuestionByIds) // 批量删除题目
|
||||
questionRouter.PUT("updateQuestion", questionApi.UpdateQuestion) // 更新题目
|
||||
}
|
||||
{
|
||||
questionRouterWithoutRecord.GET("findQuestion", questionApi.FindQuestion) // 根据ID获取题目
|
||||
questionRouterWithoutRecord.GET("getQuestionList", questionApi.GetQuestionList) // 获取题目列表
|
||||
}
|
||||
{
|
||||
questionRouterWithoutAuth.GET("getQuestionsByKnowledgePoint", questionApi.GetQuestionsByKnowledgePoint) // 根据知识点获取题目列表(无需认证)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package learning
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/api/v1"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/middleware"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type UserExamRouter struct{}
|
||||
|
||||
// InitUserExamRouter 初始化 用户考试记录 路由信息
|
||||
func (s *UserExamRouter) InitUserExamRouter(Router *gin.RouterGroup, PublicRouter *gin.RouterGroup) {
|
||||
userExamRouter := Router.Group("userExam").Use(middleware.OperationRecord())
|
||||
userExamRouterWithoutRecord := Router.Group("userExam")
|
||||
|
||||
var userExamApi = v1.ApiGroupApp.LearningApiGroup.UserExamApi
|
||||
{
|
||||
userExamRouter.POST("createUserExam", userExamApi.CreateUserExam) // 新建用户考试记录
|
||||
userExamRouter.DELETE("deleteUserExam", userExamApi.DeleteUserExam) // 删除用户考试记录
|
||||
userExamRouter.DELETE("deleteUserExamByIds", userExamApi.DeleteUserExamByIds) // 批量删除用户考试记录
|
||||
userExamRouter.PUT("updateUserExam", userExamApi.UpdateUserExam) // 更新用户考试记录
|
||||
userExamRouter.POST("startExam", userExamApi.StartExam) // 开始考试
|
||||
userExamRouter.PUT("submitExam", userExamApi.SubmitExam) // 提交考试
|
||||
}
|
||||
{
|
||||
userExamRouterWithoutRecord.GET("findUserExam", userExamApi.FindUserExam) // 根据ID获取用户考试记录
|
||||
userExamRouterWithoutRecord.GET("getUserExamList", userExamApi.GetUserExamList) // 获取用户考试记录列表
|
||||
userExamRouterWithoutRecord.GET("getUserExamHistory", userExamApi.GetUserExamHistory) // 获取用户考试历史
|
||||
userExamRouterWithoutRecord.GET("getUserExamStatistics", userExamApi.GetUserExamStatistics) // 获取用户考试统计
|
||||
userExamRouterWithoutRecord.GET("getExamRanking", userExamApi.GetExamRanking) // 获取考试排行榜
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package learning
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/api/v1"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/middleware"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type UserLearningRouter struct{}
|
||||
|
||||
// InitUserLearningRouter 初始化 用户学习记录 路由信息
|
||||
func (s *UserLearningRouter) InitUserLearningRouter(Router *gin.RouterGroup, PublicRouter *gin.RouterGroup) {
|
||||
userLearningRouter := Router.Group("userLearning").Use(middleware.OperationRecord())
|
||||
userLearningRouterWithoutRecord := Router.Group("userLearning")
|
||||
|
||||
var userLearningApi = v1.ApiGroupApp.LearningApiGroup.UserLearningApi
|
||||
{
|
||||
userLearningRouter.POST("createUserLearning", userLearningApi.CreateUserLearning) // 新建用户学习记录
|
||||
userLearningRouter.DELETE("deleteUserLearning", userLearningApi.DeleteUserLearning) // 删除用户学习记录
|
||||
userLearningRouter.DELETE("deleteUserLearningByIds", userLearningApi.DeleteUserLearningByIds) // 批量删除用户学习记录
|
||||
userLearningRouter.PUT("updateUserLearning", userLearningApi.UpdateUserLearning) // 更新用户学习记录
|
||||
}
|
||||
{
|
||||
userLearningRouterWithoutRecord.GET("findUserLearning", userLearningApi.FindUserLearning) // 根据ID获取用户学习记录
|
||||
userLearningRouterWithoutRecord.GET("getUserLearningList", userLearningApi.GetUserLearningList) // 获取用户学习记录列表
|
||||
userLearningRouterWithoutRecord.GET("getUserProgress", userLearningApi.GetUserProgress) // 获取用户学习进度
|
||||
userLearningRouterWithoutRecord.GET("getLearningStatistics", userLearningApi.GetLearningStatistics) // 获取用户学习统计
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package learning
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/api/v1"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/middleware"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type WrongQuestionRouter struct{}
|
||||
|
||||
// InitWrongQuestionRouter 初始化 错题本 路由信息
|
||||
func (s *WrongQuestionRouter) InitWrongQuestionRouter(Router *gin.RouterGroup, PublicRouter *gin.RouterGroup) {
|
||||
wrongQuestionRouter := Router.Group("wrongQuestion").Use(middleware.OperationRecord())
|
||||
wrongQuestionRouterWithoutRecord := Router.Group("wrongQuestion")
|
||||
|
||||
var wrongQuestionApi = v1.ApiGroupApp.LearningApiGroup.WrongQuestionApi
|
||||
{
|
||||
wrongQuestionRouter.POST("createWrongQuestion", wrongQuestionApi.CreateWrongQuestion) // 新建错题记录
|
||||
wrongQuestionRouter.DELETE("deleteWrongQuestion", wrongQuestionApi.DeleteWrongQuestion) // 删除错题记录
|
||||
wrongQuestionRouter.DELETE("deleteWrongQuestionByIds", wrongQuestionApi.DeleteWrongQuestionByIds) // 批量删除错题记录
|
||||
wrongQuestionRouter.PUT("updateWrongQuestion", wrongQuestionApi.UpdateWrongQuestion) // 更新错题记录
|
||||
wrongQuestionRouter.PUT("markAsMastered", wrongQuestionApi.MarkAsMastered) // 标记为已掌握
|
||||
}
|
||||
{
|
||||
wrongQuestionRouterWithoutRecord.GET("findWrongQuestion", wrongQuestionApi.FindWrongQuestion) // 根据ID获取错题记录
|
||||
wrongQuestionRouterWithoutRecord.GET("getWrongQuestionList", wrongQuestionApi.GetWrongQuestionList) // 获取错题记录列表
|
||||
wrongQuestionRouterWithoutRecord.GET("getUserWrongQuestions", wrongQuestionApi.GetUserWrongQuestions) // 获取用户错题列表
|
||||
wrongQuestionRouterWithoutRecord.GET("getWrongQuestionStatistics", wrongQuestionApi.GetWrongQuestionStatistics) // 获取错题统计
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,14 @@ package service
|
||||
|
||||
import (
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/service/example"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/service/learning"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/service/system"
|
||||
)
|
||||
|
||||
var ServiceGroupApp = new(ServiceGroup)
|
||||
|
||||
type ServiceGroup struct {
|
||||
SystemServiceGroup system.ServiceGroup
|
||||
ExampleServiceGroup example.ServiceGroup
|
||||
SystemServiceGroup system.ServiceGroup
|
||||
ExampleServiceGroup example.ServiceGroup
|
||||
LearningServiceGroup learning.ServiceGroup
|
||||
}
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
package learning
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/learning"
|
||||
learningReq "github.com/flipped-aurora/gin-vue-admin/server/model/learning/request"
|
||||
)
|
||||
|
||||
type ChapterService struct{}
|
||||
|
||||
// CreateChapter 创建章节
|
||||
func (chapterService *ChapterService) CreateChapter(ctx context.Context, chapter *learning.Chapter) (err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Create(chapter).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteChapter 删除章节
|
||||
func (chapterService *ChapterService) DeleteChapter(ctx context.Context, ID string) (err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Delete(&learning.Chapter{}, ID).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteChapterByIds 批量删除章节
|
||||
func (chapterService *ChapterService) DeleteChapterByIds(ctx context.Context, IDs []string) (err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Delete(&[]learning.Chapter{}, "id in ?", IDs).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateChapter 更新章节
|
||||
func (chapterService *ChapterService) UpdateChapter(ctx context.Context, chapter learning.Chapter) (err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Model(&learning.Chapter{}).Where("id = ?", chapter.ID).Updates(&chapter).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// GetChapter 根据ID获取章节
|
||||
func (chapterService *ChapterService) GetChapter(ctx context.Context, ID string) (chapter learning.Chapter, err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Where("id = ?", ID).First(&chapter).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// 手动查询关联的课程信息
|
||||
if chapter.CourseId != 0 {
|
||||
var course learning.Course
|
||||
courseErr := global.GVA_DB.WithContext(ctx).Where("id = ?", chapter.CourseId).First(&course).Error
|
||||
if courseErr == nil {
|
||||
chapter.Course = course
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetChapterInfoList 分页获取章节列表
|
||||
func (chapterService *ChapterService) GetChapterInfoList(ctx context.Context, info learningReq.ChapterSearch) (list []learning.Chapter, total int64, err error) {
|
||||
limit := info.PageSize
|
||||
offset := info.PageSize * (info.Page - 1)
|
||||
// 创建db
|
||||
db := global.GVA_DB.WithContext(ctx).Model(&learning.Chapter{})
|
||||
var chapters []learning.Chapter
|
||||
// 如果有条件搜索 下方会自动创建搜索语句
|
||||
if info.StartCreatedAt != nil && info.EndCreatedAt != nil {
|
||||
db = db.Where("created_at BETWEEN ? AND ?", info.StartCreatedAt, info.EndCreatedAt)
|
||||
}
|
||||
if info.Title != "" {
|
||||
db = db.Where("title LIKE ?", "%"+info.Title+"%")
|
||||
}
|
||||
if info.Status != "" {
|
||||
db = db.Where("status = ?", info.Status)
|
||||
}
|
||||
if info.CourseId != 0 {
|
||||
db = db.Where("course_id = ?", info.CourseId)
|
||||
}
|
||||
err = db.Count(&total).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if limit != 0 {
|
||||
db = db.Limit(limit).Offset(offset)
|
||||
}
|
||||
|
||||
err = db.Order("sort asc, created_at desc").Find(&chapters).Error
|
||||
if err != nil {
|
||||
return chapters, total, err
|
||||
}
|
||||
|
||||
// 手动查询关联的课程信息
|
||||
courseIds := make([]uint, 0)
|
||||
courseMap := make(map[uint]learning.Course)
|
||||
|
||||
for _, chapter := range chapters {
|
||||
if chapter.CourseId != 0 {
|
||||
courseIds = append(courseIds, chapter.CourseId)
|
||||
}
|
||||
}
|
||||
|
||||
if len(courseIds) > 0 {
|
||||
var courses []learning.Course
|
||||
global.GVA_DB.WithContext(ctx).Where("id in ?", courseIds).Find(&courses)
|
||||
for _, course := range courses {
|
||||
courseMap[course.ID] = course
|
||||
}
|
||||
|
||||
// 为每个章节设置课程信息
|
||||
for i := range chapters {
|
||||
if course, exists := courseMap[chapters[i].CourseId]; exists {
|
||||
chapters[i].Course = course
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return chapters, total, err
|
||||
}
|
||||
|
||||
// GetChaptersByCourse 根据课程ID获取章节列表
|
||||
func (chapterService *ChapterService) GetChaptersByCourse(ctx context.Context, courseId uint) (list []learning.Chapter, err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Where("course_id = ? AND status = ?", courseId, "published").Order("sort asc, created_at asc").Find(&list).Error
|
||||
return
|
||||
}
|
||||
|
||||
// UpdateChapterStatus 更新章节状态
|
||||
func (chapterService *ChapterService) UpdateChapterStatus(ctx context.Context, ID string, status string) (err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Model(&learning.Chapter{}).Where("id = ?", ID).Update("status", status).Error
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package learning
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/learning"
|
||||
learningReq "github.com/flipped-aurora/gin-vue-admin/server/model/learning/request"
|
||||
)
|
||||
|
||||
type CourseService struct{}
|
||||
|
||||
// CreateCourse 创建课程
|
||||
func (courseService *CourseService) CreateCourse(ctx context.Context, course *learning.Course) (err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Create(course).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteCourse 删除课程
|
||||
func (courseService *CourseService) DeleteCourse(ctx context.Context, ID string) (err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Delete(&learning.Course{}, ID).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteCourseByIds 批量删除课程
|
||||
func (courseService *CourseService) DeleteCourseByIds(ctx context.Context, IDs []string) (err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Delete(&[]learning.Course{}, "id in ?", IDs).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateCourse 更新课程
|
||||
func (courseService *CourseService) UpdateCourse(ctx context.Context, course learning.Course) (err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Model(&learning.Course{}).Where("id = ?", course.ID).Updates(&course).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// GetCourse 根据ID获取课程
|
||||
func (courseService *CourseService) GetCourse(ctx context.Context, ID string) (course learning.Course, err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Where("id = ?", ID).First(&course).Error
|
||||
return
|
||||
}
|
||||
|
||||
// GetCourseInfoList 分页获取课程列表
|
||||
func (courseService *CourseService) GetCourseInfoList(ctx context.Context, info learningReq.CourseSearch) (list []learning.Course, total int64, err error) {
|
||||
limit := info.PageSize
|
||||
offset := info.PageSize * (info.Page - 1)
|
||||
// 创建db
|
||||
db := global.GVA_DB.WithContext(ctx).Model(&learning.Course{})
|
||||
var courses []learning.Course
|
||||
// 如果有条件搜索 下方会自动创建搜索语句
|
||||
if info.StartCreatedAt != nil && info.EndCreatedAt != nil {
|
||||
db = db.Where("created_at BETWEEN ? AND ?", info.StartCreatedAt, info.EndCreatedAt)
|
||||
}
|
||||
if info.Title != "" {
|
||||
db = db.Where("title LIKE ?", "%"+info.Title+"%")
|
||||
}
|
||||
if info.Status != "" {
|
||||
db = db.Where("status = ?", info.Status)
|
||||
}
|
||||
if info.Difficulty != "" {
|
||||
db = db.Where("difficulty = ?", info.Difficulty)
|
||||
}
|
||||
err = db.Count(&total).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if limit != 0 {
|
||||
db = db.Limit(limit).Offset(offset)
|
||||
}
|
||||
|
||||
err = db.Order("sort desc, created_at desc").Find(&courses).Error
|
||||
return courses, total, err
|
||||
}
|
||||
|
||||
// GetPublishedCourses 获取已发布的课程列表(供学员选择)
|
||||
func (courseService *CourseService) GetPublishedCourses(ctx context.Context) (list []learning.Course, err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Where("status = ?", "published").Order("sort desc, created_at desc").Find(&list).Error
|
||||
return
|
||||
}
|
||||
|
||||
// UpdateCourseStatus 更新课程状态
|
||||
func (courseService *CourseService) UpdateCourseStatus(ctx context.Context, ID string, status string) (err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Model(&learning.Course{}).Where("id = ?", ID).Update("status", status).Error
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package learning
|
||||
|
||||
type ServiceGroup struct {
|
||||
CourseService
|
||||
ChapterService
|
||||
KnowledgePointService
|
||||
QuestionService
|
||||
UserLearningService
|
||||
WrongQuestionService
|
||||
ExamService
|
||||
UserExamService
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
package learning
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/learning"
|
||||
learningReq "github.com/flipped-aurora/gin-vue-admin/server/model/learning/request"
|
||||
)
|
||||
|
||||
type ExamService struct{}
|
||||
|
||||
// CreateExam 创建考试
|
||||
func (examService *ExamService) CreateExam(ctx context.Context, exam *learning.Exam) (err error) {
|
||||
// 设置默认值
|
||||
if exam.Status == "" {
|
||||
exam.Status = "draft"
|
||||
}
|
||||
if exam.MaxAttempts == 0 {
|
||||
exam.MaxAttempts = 1
|
||||
}
|
||||
|
||||
err = global.GVA_DB.WithContext(ctx).Create(exam).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteExam 删除考试
|
||||
func (examService *ExamService) DeleteExam(ctx context.Context, ID string) (err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Delete(&learning.Exam{}, ID).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteExamByIds 批量删除考试
|
||||
func (examService *ExamService) DeleteExamByIds(ctx context.Context, IDs []string) (err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Delete(&[]learning.Exam{}, "id in ?", IDs).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateExam 更新考试
|
||||
func (examService *ExamService) UpdateExam(ctx context.Context, exam learning.Exam) (err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Model(&learning.Exam{}).Where("id = ?", exam.ID).Updates(&exam).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// GetExam 根据ID获取考试
|
||||
func (examService *ExamService) GetExam(ctx context.Context, ID string) (exam learning.Exam, err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Where("id = ?", ID).First(&exam).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// 手动查询关联的课程信息
|
||||
examService.loadCourseInfo(ctx, &exam)
|
||||
return
|
||||
}
|
||||
|
||||
// GetExamInfoList 分页获取考试列表
|
||||
func (examService *ExamService) GetExamInfoList(ctx context.Context, info learningReq.ExamSearch) (list []learning.Exam, total int64, err error) {
|
||||
limit := info.PageSize
|
||||
offset := info.PageSize * (info.Page - 1)
|
||||
// 创建db
|
||||
db := global.GVA_DB.WithContext(ctx).Model(&learning.Exam{})
|
||||
var exams []learning.Exam
|
||||
// 如果有条件搜索 下方会自动创建搜索语句
|
||||
if info.StartCreatedAt != nil && info.EndCreatedAt != nil {
|
||||
db = db.Where("created_at BETWEEN ? AND ?", info.StartCreatedAt, info.EndCreatedAt)
|
||||
}
|
||||
if info.Title != "" {
|
||||
db = db.Where("title LIKE ?", "%"+info.Title+"%")
|
||||
}
|
||||
if info.CourseId != 0 {
|
||||
db = db.Where("course_id = ?", info.CourseId)
|
||||
}
|
||||
if info.Status != "" {
|
||||
db = db.Where("status = ?", info.Status)
|
||||
}
|
||||
err = db.Count(&total).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if limit != 0 {
|
||||
db = db.Limit(limit).Offset(offset)
|
||||
}
|
||||
|
||||
err = db.Order("created_at desc").Find(&exams).Error
|
||||
if err != nil {
|
||||
return exams, total, err
|
||||
}
|
||||
|
||||
// 批量加载关联的课程信息
|
||||
for i := range exams {
|
||||
examService.loadCourseInfo(ctx, &exams[i])
|
||||
}
|
||||
|
||||
return exams, total, err
|
||||
}
|
||||
|
||||
// GetActiveExams 获取进行中的考试列表
|
||||
func (examService *ExamService) GetActiveExams(ctx context.Context) (list []learning.Exam, err error) {
|
||||
now := time.Now()
|
||||
err = global.GVA_DB.WithContext(ctx).
|
||||
Where("status = ? AND start_time <= ? AND end_time >= ?", "published", now, now).
|
||||
Order("start_time asc").
|
||||
Find(&list).Error
|
||||
|
||||
// 批量加载关联的课程信息
|
||||
for i := range list {
|
||||
examService.loadCourseInfo(ctx, &list[i])
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GetExamsByCourse 根据课程获取考试列表
|
||||
func (examService *ExamService) GetExamsByCourse(ctx context.Context, courseId uint) (list []learning.Exam, err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).
|
||||
Where("course_id = ? AND status = ?", courseId, "published").
|
||||
Order("start_time asc").
|
||||
Find(&list).Error
|
||||
|
||||
// 批量加载关联的课程信息
|
||||
for i := range list {
|
||||
examService.loadCourseInfo(ctx, &list[i])
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// PublishExam 发布考试
|
||||
func (examService *ExamService) PublishExam(ctx context.Context, examId uint) (err error) {
|
||||
updates := map[string]interface{}{
|
||||
"status": "published",
|
||||
}
|
||||
err = global.GVA_DB.WithContext(ctx).Model(&learning.Exam{}).
|
||||
Where("id = ?", examId).
|
||||
Updates(updates).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// GetExamStatistics 获取考试统计
|
||||
func (examService *ExamService) GetExamStatistics(ctx context.Context) (stats learningReq.ExamStatistics, err error) {
|
||||
// 总考试数
|
||||
var totalExams int64
|
||||
err = global.GVA_DB.WithContext(ctx).Model(&learning.Exam{}).Count(&totalExams).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
stats.TotalExams = int(totalExams)
|
||||
|
||||
// 进行中的考试数
|
||||
now := time.Now()
|
||||
var activeExams int64
|
||||
err = global.GVA_DB.WithContext(ctx).Model(&learning.Exam{}).
|
||||
Where("status = ? AND start_time <= ? AND end_time >= ?", "published", now, now).
|
||||
Count(&activeExams).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
stats.ActiveExams = int(activeExams)
|
||||
|
||||
// 已结束的考试数
|
||||
var completedExams int64
|
||||
err = global.GVA_DB.WithContext(ctx).Model(&learning.Exam{}).
|
||||
Where("status = ? AND end_time < ?", "published", now).
|
||||
Count(&completedExams).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
stats.CompletedExams = int(completedExams)
|
||||
|
||||
// 草稿考试数
|
||||
var draftExams int64
|
||||
err = global.GVA_DB.WithContext(ctx).Model(&learning.Exam{}).
|
||||
Where("status = ?", "draft").
|
||||
Count(&draftExams).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
stats.DraftExams = int(draftExams)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// loadCourseInfo 加载课程信息
|
||||
func (examService *ExamService) loadCourseInfo(ctx context.Context, exam *learning.Exam) {
|
||||
if exam.CourseId != 0 {
|
||||
var course learning.Course
|
||||
if err := global.GVA_DB.WithContext(ctx).Where("id = ?", exam.CourseId).First(&course).Error; err == nil {
|
||||
exam.Course = course
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
package learning
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/learning"
|
||||
learningReq "github.com/flipped-aurora/gin-vue-admin/server/model/learning/request"
|
||||
)
|
||||
|
||||
type KnowledgePointService struct{}
|
||||
|
||||
// CreateKnowledgePoint 创建知识点
|
||||
func (knowledgePointService *KnowledgePointService) CreateKnowledgePoint(ctx context.Context, knowledgePoint *learning.KnowledgePoint) (err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Create(knowledgePoint).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteKnowledgePoint 删除知识点
|
||||
func (knowledgePointService *KnowledgePointService) DeleteKnowledgePoint(ctx context.Context, ID string) (err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Delete(&learning.KnowledgePoint{}, ID).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteKnowledgePointByIds 批量删除知识点
|
||||
func (knowledgePointService *KnowledgePointService) DeleteKnowledgePointByIds(ctx context.Context, IDs []string) (err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Delete(&[]learning.KnowledgePoint{}, "id in ?", IDs).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateKnowledgePoint 更新知识点
|
||||
func (knowledgePointService *KnowledgePointService) UpdateKnowledgePoint(ctx context.Context, knowledgePoint learning.KnowledgePoint) (err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Model(&learning.KnowledgePoint{}).Where("id = ?", knowledgePoint.ID).Updates(&knowledgePoint).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// GetKnowledgePoint 根据ID获取知识点
|
||||
func (knowledgePointService *KnowledgePointService) GetKnowledgePoint(ctx context.Context, ID string) (knowledgePoint learning.KnowledgePoint, err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Where("id = ?", ID).First(&knowledgePoint).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// 手动查询关联的章节信息
|
||||
if knowledgePoint.ChapterId != 0 {
|
||||
var chapter learning.Chapter
|
||||
chapterErr := global.GVA_DB.WithContext(ctx).Where("id = ?", knowledgePoint.ChapterId).First(&chapter).Error
|
||||
if chapterErr == nil {
|
||||
knowledgePoint.Chapter = chapter
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetKnowledgePointInfoList 分页获取知识点列表
|
||||
func (knowledgePointService *KnowledgePointService) GetKnowledgePointInfoList(ctx context.Context, info learningReq.KnowledgePointSearch) (list []learning.KnowledgePoint, total int64, err error) {
|
||||
limit := info.PageSize
|
||||
offset := info.PageSize * (info.Page - 1)
|
||||
// 创建db
|
||||
db := global.GVA_DB.WithContext(ctx).Model(&learning.KnowledgePoint{})
|
||||
var knowledgePoints []learning.KnowledgePoint
|
||||
// 如果有条件搜索 下方会自动创建搜索语句
|
||||
if info.StartCreatedAt != nil && info.EndCreatedAt != nil {
|
||||
db = db.Where("created_at BETWEEN ? AND ?", info.StartCreatedAt, info.EndCreatedAt)
|
||||
}
|
||||
if info.Title != "" {
|
||||
db = db.Where("title LIKE ?", "%"+info.Title+"%")
|
||||
}
|
||||
if info.Status != "" {
|
||||
db = db.Where("status = ?", info.Status)
|
||||
}
|
||||
if info.ChapterId != 0 {
|
||||
db = db.Where("chapter_id = ?", info.ChapterId)
|
||||
}
|
||||
err = db.Count(&total).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if limit != 0 {
|
||||
db = db.Limit(limit).Offset(offset)
|
||||
}
|
||||
|
||||
err = db.Order("sort asc, created_at desc").Find(&knowledgePoints).Error
|
||||
if err != nil {
|
||||
return knowledgePoints, total, err
|
||||
}
|
||||
|
||||
// 手动查询关联的章节信息
|
||||
chapterIds := make([]uint, 0)
|
||||
chapterMap := make(map[uint]learning.Chapter)
|
||||
|
||||
for _, kp := range knowledgePoints {
|
||||
if kp.ChapterId != 0 {
|
||||
chapterIds = append(chapterIds, kp.ChapterId)
|
||||
}
|
||||
}
|
||||
|
||||
if len(chapterIds) > 0 {
|
||||
var chapters []learning.Chapter
|
||||
global.GVA_DB.WithContext(ctx).Where("id in ?", chapterIds).Find(&chapters)
|
||||
for _, chapter := range chapters {
|
||||
chapterMap[chapter.ID] = chapter
|
||||
}
|
||||
|
||||
// 为每个知识点设置章节信息
|
||||
for i := range knowledgePoints {
|
||||
if chapter, exists := chapterMap[knowledgePoints[i].ChapterId]; exists {
|
||||
knowledgePoints[i].Chapter = chapter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return knowledgePoints, total, err
|
||||
}
|
||||
|
||||
// GetKnowledgePointsByChapter 根据章节ID获取知识点列表
|
||||
func (knowledgePointService *KnowledgePointService) GetKnowledgePointsByChapter(ctx context.Context, chapterId uint) (list []learning.KnowledgePoint, err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Where("chapter_id = ? AND status = ?", chapterId, "published").Order("sort asc, created_at asc").Find(&list).Error
|
||||
return
|
||||
}
|
||||
|
||||
// UpdateKnowledgePointStatus 更新知识点状态
|
||||
func (knowledgePointService *KnowledgePointService) UpdateKnowledgePointStatus(ctx context.Context, ID string, status string) (err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Model(&learning.KnowledgePoint{}).Where("id = ?", ID).Update("status", status).Error
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
package learning
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/learning"
|
||||
learningReq "github.com/flipped-aurora/gin-vue-admin/server/model/learning/request"
|
||||
)
|
||||
|
||||
type QuestionService struct{}
|
||||
|
||||
// CreateQuestion 创建题目
|
||||
func (questionService *QuestionService) CreateQuestion(ctx context.Context, question *learning.Question) (err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Create(question).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteQuestion 删除题目
|
||||
func (questionService *QuestionService) DeleteQuestion(ctx context.Context, ID string) (err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Delete(&learning.Question{}, ID).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteQuestionByIds 批量删除题目
|
||||
func (questionService *QuestionService) DeleteQuestionByIds(ctx context.Context, IDs []string) (err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Delete(&[]learning.Question{}, "id in ?", IDs).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateQuestion 更新题目
|
||||
func (questionService *QuestionService) UpdateQuestion(ctx context.Context, question learning.Question) (err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Model(&learning.Question{}).Where("id = ?", question.ID).Updates(&question).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// GetQuestion 根据ID获取题目
|
||||
func (questionService *QuestionService) GetQuestion(ctx context.Context, ID string) (question learning.Question, err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Where("id = ?", ID).First(&question).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// 手动查询关联的知识点信息
|
||||
if question.KnowledgePointId != 0 {
|
||||
var knowledgePoint learning.KnowledgePoint
|
||||
kpErr := global.GVA_DB.WithContext(ctx).Where("id = ?", question.KnowledgePointId).First(&knowledgePoint).Error
|
||||
if kpErr == nil {
|
||||
question.KnowledgePoint = knowledgePoint
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetQuestionInfoList 分页获取题目列表
|
||||
func (questionService *QuestionService) GetQuestionInfoList(ctx context.Context, info learningReq.QuestionSearch) (list []learning.Question, total int64, err error) {
|
||||
limit := info.PageSize
|
||||
offset := info.PageSize * (info.Page - 1)
|
||||
// 创建db
|
||||
db := global.GVA_DB.WithContext(ctx).Model(&learning.Question{})
|
||||
var questions []learning.Question
|
||||
// 如果有条件搜索 下方会自动创建搜索语句
|
||||
if info.StartCreatedAt != nil && info.EndCreatedAt != nil {
|
||||
db = db.Where("created_at BETWEEN ? AND ?", info.StartCreatedAt, info.EndCreatedAt)
|
||||
}
|
||||
if info.Title != "" {
|
||||
db = db.Where("title LIKE ?", "%"+info.Title+"%")
|
||||
}
|
||||
if info.Type != "" {
|
||||
db = db.Where("type = ?", info.Type)
|
||||
}
|
||||
if info.Difficulty != "" {
|
||||
db = db.Where("difficulty = ?", info.Difficulty)
|
||||
}
|
||||
if info.KnowledgePointId != 0 {
|
||||
db = db.Where("knowledge_point_id = ?", info.KnowledgePointId)
|
||||
}
|
||||
err = db.Count(&total).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if limit != 0 {
|
||||
db = db.Limit(limit).Offset(offset)
|
||||
}
|
||||
|
||||
err = db.Order("created_at desc").Find(&questions).Error
|
||||
if err != nil {
|
||||
return questions, total, err
|
||||
}
|
||||
|
||||
// 手动查询关联的知识点信息
|
||||
kpIds := make([]uint, 0)
|
||||
kpMap := make(map[uint]learning.KnowledgePoint)
|
||||
|
||||
for _, q := range questions {
|
||||
if q.KnowledgePointId != 0 {
|
||||
kpIds = append(kpIds, q.KnowledgePointId)
|
||||
}
|
||||
}
|
||||
|
||||
if len(kpIds) > 0 {
|
||||
var knowledgePoints []learning.KnowledgePoint
|
||||
global.GVA_DB.WithContext(ctx).Where("id in ?", kpIds).Find(&knowledgePoints)
|
||||
for _, kp := range knowledgePoints {
|
||||
kpMap[kp.ID] = kp
|
||||
}
|
||||
|
||||
// 为每个题目设置知识点信息
|
||||
for i := range questions {
|
||||
if kp, exists := kpMap[questions[i].KnowledgePointId]; exists {
|
||||
questions[i].KnowledgePoint = kp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return questions, total, err
|
||||
}
|
||||
|
||||
// GetQuestionsByKnowledgePoint 根据知识点ID获取题目列表
|
||||
func (questionService *QuestionService) GetQuestionsByKnowledgePoint(ctx context.Context, knowledgePointId uint) (list []learning.Question, err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Where("knowledge_point_id = ?", knowledgePointId).Order("created_at asc").Find(&list).Error
|
||||
return
|
||||
}
|
||||
|
||||
// GetRandomQuestionsByKnowledgePoint 根据知识点ID随机获取题目
|
||||
func (questionService *QuestionService) GetRandomQuestionsByKnowledgePoint(ctx context.Context, knowledgePointId uint, count int) (list []learning.Question, err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Where("knowledge_point_id = ?", knowledgePointId).Order("RAND()").Limit(count).Find(&list).Error
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,327 @@
|
||||
package learning
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/learning"
|
||||
learningReq "github.com/flipped-aurora/gin-vue-admin/server/model/learning/request"
|
||||
)
|
||||
|
||||
type UserExamService struct{}
|
||||
|
||||
// CreateUserExam 创建用户考试记录
|
||||
func (userExamService *UserExamService) CreateUserExam(ctx context.Context, userExam *learning.UserExam) (err error) {
|
||||
// 设置默认值
|
||||
if userExam.Status == "" {
|
||||
userExam.Status = "in_progress"
|
||||
}
|
||||
if userExam.AttemptNumber == 0 {
|
||||
userExam.AttemptNumber = 1
|
||||
}
|
||||
if userExam.StartTime.IsZero() {
|
||||
userExam.StartTime = time.Now()
|
||||
}
|
||||
|
||||
err = global.GVA_DB.WithContext(ctx).Create(userExam).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteUserExam 删除用户考试记录
|
||||
func (userExamService *UserExamService) DeleteUserExam(ctx context.Context, ID string) (err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Delete(&learning.UserExam{}, ID).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteUserExamByIds 批量删除用户考试记录
|
||||
func (userExamService *UserExamService) DeleteUserExamByIds(ctx context.Context, IDs []string) (err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Delete(&[]learning.UserExam{}, "id in ?", IDs).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateUserExam 更新用户考试记录
|
||||
func (userExamService *UserExamService) UpdateUserExam(ctx context.Context, userExam learning.UserExam) (err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Model(&learning.UserExam{}).Where("id = ?", userExam.ID).Updates(&userExam).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// GetUserExam 根据ID获取用户考试记录
|
||||
func (userExamService *UserExamService) GetUserExam(ctx context.Context, ID string) (userExam learning.UserExam, err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Where("id = ?", ID).First(&userExam).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// 手动查询关联的考试信息
|
||||
userExamService.loadExamInfo(ctx, &userExam)
|
||||
return
|
||||
}
|
||||
|
||||
// GetUserExamInfoList 分页获取用户考试记录列表
|
||||
func (userExamService *UserExamService) GetUserExamInfoList(ctx context.Context, info learningReq.UserExamSearch) (list []learning.UserExam, total int64, err error) {
|
||||
limit := info.PageSize
|
||||
offset := info.PageSize * (info.Page - 1)
|
||||
// 创建db
|
||||
db := global.GVA_DB.WithContext(ctx).Model(&learning.UserExam{})
|
||||
var userExams []learning.UserExam
|
||||
// 如果有条件搜索 下方会自动创建搜索语句
|
||||
if info.StartCreatedAt != nil && info.EndCreatedAt != nil {
|
||||
db = db.Where("created_at BETWEEN ? AND ?", info.StartCreatedAt, info.EndCreatedAt)
|
||||
}
|
||||
if info.UserId != 0 {
|
||||
db = db.Where("user_id = ?", info.UserId)
|
||||
}
|
||||
if info.ExamId != 0 {
|
||||
db = db.Where("exam_id = ?", info.ExamId)
|
||||
}
|
||||
if info.Status != "" {
|
||||
db = db.Where("status = ?", info.Status)
|
||||
}
|
||||
if info.IsPassed {
|
||||
db = db.Where("is_passed = ?", info.IsPassed)
|
||||
}
|
||||
err = db.Count(&total).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if limit != 0 {
|
||||
db = db.Limit(limit).Offset(offset)
|
||||
}
|
||||
|
||||
err = db.Order("created_at desc").Find(&userExams).Error
|
||||
if err != nil {
|
||||
return userExams, total, err
|
||||
}
|
||||
|
||||
// 批量加载关联的考试信息
|
||||
for i := range userExams {
|
||||
userExamService.loadExamInfo(ctx, &userExams[i])
|
||||
}
|
||||
|
||||
return userExams, total, err
|
||||
}
|
||||
|
||||
// StartExam 开始考试
|
||||
func (userExamService *UserExamService) StartExam(ctx context.Context, userId uint, examId uint) (userExam learning.UserExam, err error) {
|
||||
// 检查考试是否存在且可以参加
|
||||
var exam learning.Exam
|
||||
err = global.GVA_DB.WithContext(ctx).Where("id = ? AND status = ?", examId, "published").First(&exam).Error
|
||||
if err != nil {
|
||||
return userExam, errors.New("考试不存在或未发布")
|
||||
}
|
||||
|
||||
// 检查考试时间
|
||||
now := time.Now()
|
||||
if now.Before(exam.StartTime) {
|
||||
return userExam, errors.New("考试尚未开始")
|
||||
}
|
||||
if now.After(exam.EndTime) {
|
||||
return userExam, errors.New("考试已结束")
|
||||
}
|
||||
|
||||
// 检查用户是否已经参加过考试
|
||||
var existingCount int64
|
||||
global.GVA_DB.WithContext(ctx).Model(&learning.UserExam{}).
|
||||
Where("user_id = ? AND exam_id = ?", userId, examId).
|
||||
Count(&existingCount)
|
||||
|
||||
// 检查是否允许重考和尝试次数
|
||||
if existingCount > 0 && !exam.AllowRetake {
|
||||
return userExam, errors.New("该考试不允许重考")
|
||||
}
|
||||
if existingCount >= int64(exam.MaxAttempts) {
|
||||
return userExam, errors.New("已达到最大尝试次数")
|
||||
}
|
||||
|
||||
// 创建考试记录
|
||||
userExam = learning.UserExam{
|
||||
UserId: userId,
|
||||
ExamId: examId,
|
||||
AttemptNumber: int(existingCount) + 1,
|
||||
StartTime: now,
|
||||
Status: "in_progress",
|
||||
}
|
||||
|
||||
err = userExamService.CreateUserExam(ctx, &userExam)
|
||||
if err != nil {
|
||||
return userExam, err
|
||||
}
|
||||
|
||||
// 加载考试信息
|
||||
userExamService.loadExamInfo(ctx, &userExam)
|
||||
|
||||
return userExam, nil
|
||||
}
|
||||
|
||||
// SubmitExam 提交考试
|
||||
func (userExamService *UserExamService) SubmitExam(ctx context.Context, userExamId uint, totalScore int, correctCount int, wrongCount int) (err error) {
|
||||
var userExam learning.UserExam
|
||||
err = global.GVA_DB.WithContext(ctx).Where("id = ?", userExamId).First(&userExam).Error
|
||||
if err != nil {
|
||||
return errors.New("考试记录不存在")
|
||||
}
|
||||
|
||||
if userExam.Status != "in_progress" {
|
||||
return errors.New("考试已结束,无法重复提交")
|
||||
}
|
||||
|
||||
// 获取考试信息
|
||||
var exam learning.Exam
|
||||
err = global.GVA_DB.WithContext(ctx).Where("id = ?", userExam.ExamId).First(&exam).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 计算考试时长
|
||||
now := time.Now()
|
||||
duration := int(now.Sub(userExam.StartTime).Minutes())
|
||||
|
||||
// 判断是否通过
|
||||
isPassed := totalScore >= exam.PassScore
|
||||
|
||||
// 更新考试记录
|
||||
updates := map[string]interface{}{
|
||||
"end_time": now,
|
||||
"submitted_at": now,
|
||||
"duration": duration,
|
||||
"total_score": totalScore,
|
||||
"correct_count": correctCount,
|
||||
"wrong_count": wrongCount,
|
||||
"status": "completed",
|
||||
"is_passed": isPassed,
|
||||
}
|
||||
|
||||
err = global.GVA_DB.WithContext(ctx).Model(&learning.UserExam{}).
|
||||
Where("id = ?", userExamId).
|
||||
Updates(updates).Error
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// GetUserExamHistory 获取用户考试历史
|
||||
func (userExamService *UserExamService) GetUserExamHistory(ctx context.Context, userId uint) (list []learning.UserExam, err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).
|
||||
Where("user_id = ?", userId).
|
||||
Order("created_at desc").
|
||||
Find(&list).Error
|
||||
|
||||
// 批量加载关联的考试信息
|
||||
for i := range list {
|
||||
userExamService.loadExamInfo(ctx, &list[i])
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GetUserExamStatistics 获取用户考试统计
|
||||
func (userExamService *UserExamService) GetUserExamStatistics(ctx context.Context, userId uint) (stats learningReq.UserExamStatistics, err error) {
|
||||
// 总考试次数
|
||||
var totalExams int64
|
||||
err = global.GVA_DB.WithContext(ctx).Model(&learning.UserExam{}).
|
||||
Where("user_id = ? AND status = ?", userId, "completed").
|
||||
Count(&totalExams).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
stats.TotalExams = int(totalExams)
|
||||
|
||||
if totalExams == 0 {
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
// 通过考试次数
|
||||
var passedExams int64
|
||||
err = global.GVA_DB.WithContext(ctx).Model(&learning.UserExam{}).
|
||||
Where("user_id = ? AND status = ? AND is_passed = ?", userId, "completed", true).
|
||||
Count(&passedExams).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
stats.PassedExams = int(passedExams)
|
||||
stats.FailedExams = stats.TotalExams - stats.PassedExams
|
||||
|
||||
// 平均分数和最高分数
|
||||
var result struct {
|
||||
AverageScore float64
|
||||
HighestScore int
|
||||
TotalTime int
|
||||
}
|
||||
err = global.GVA_DB.WithContext(ctx).Model(&learning.UserExam{}).
|
||||
Select("AVG(total_score) as average_score, MAX(total_score) as highest_score, SUM(duration) as total_time").
|
||||
Where("user_id = ? AND status = ?", userId, "completed").
|
||||
Scan(&result).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
stats.AverageScore = result.AverageScore
|
||||
stats.HighestScore = result.HighestScore
|
||||
stats.TotalStudyTime = result.TotalTime
|
||||
|
||||
// 通过率
|
||||
if stats.TotalExams > 0 {
|
||||
stats.PassRate = float64(stats.PassedExams) / float64(stats.TotalExams) * 100
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GetExamRanking 获取考试排行榜
|
||||
func (userExamService *UserExamService) GetExamRanking(ctx context.Context, examId uint, limit int) (ranking learningReq.ExamRankingResponse, err error) {
|
||||
// 获取考试信息
|
||||
var exam learning.Exam
|
||||
err = global.GVA_DB.WithContext(ctx).Where("id = ?", examId).First(&exam).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
ranking.ExamId = examId
|
||||
ranking.ExamTitle = exam.Title
|
||||
|
||||
// 获取排行榜数据
|
||||
var userExams []learning.UserExam
|
||||
db := global.GVA_DB.WithContext(ctx).
|
||||
Where("exam_id = ? AND status = ?", examId, "completed").
|
||||
Order("total_score desc, duration asc")
|
||||
|
||||
if limit > 0 {
|
||||
db = db.Limit(limit)
|
||||
}
|
||||
|
||||
err = db.Find(&userExams).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 构建排行榜
|
||||
for i, userExam := range userExams {
|
||||
item := learningReq.ExamRankingItem{
|
||||
UserId: userExam.UserId,
|
||||
Score: userExam.TotalScore,
|
||||
Duration: userExam.Duration,
|
||||
Rank: i + 1,
|
||||
IsPassed: userExam.IsPassed,
|
||||
SubmittedAt: userExam.SubmittedAt,
|
||||
}
|
||||
ranking.Rankings = append(ranking.Rankings, item)
|
||||
}
|
||||
|
||||
// 获取总数
|
||||
global.GVA_DB.WithContext(ctx).Model(&learning.UserExam{}).
|
||||
Where("exam_id = ? AND status = ?", examId, "completed").
|
||||
Count(&ranking.Total)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// loadExamInfo 加载考试信息
|
||||
func (userExamService *UserExamService) loadExamInfo(ctx context.Context, userExam *learning.UserExam) {
|
||||
if userExam.ExamId != 0 {
|
||||
var exam learning.Exam
|
||||
if err := global.GVA_DB.WithContext(ctx).Where("id = ?", userExam.ExamId).First(&exam).Error; err == nil {
|
||||
userExam.Exam = exam
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
package learning
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/learning"
|
||||
learningReq "github.com/flipped-aurora/gin-vue-admin/server/model/learning/request"
|
||||
)
|
||||
|
||||
type UserLearningService struct{}
|
||||
|
||||
// CreateUserLearning 创建用户学习记录
|
||||
func (userLearningService *UserLearningService) CreateUserLearning(ctx context.Context, userLearning *learning.UserLearning) (err error) {
|
||||
// 检查是否已存在该用户对该课程的学习记录
|
||||
var existingRecord learning.UserLearning
|
||||
err = global.GVA_DB.WithContext(ctx).Where("user_id = ? AND course_id = ?", userLearning.UserId, userLearning.CourseId).First(&existingRecord).Error
|
||||
if err == nil {
|
||||
// 如果已存在,更新记录
|
||||
return userLearningService.UpdateUserLearning(ctx, *userLearning)
|
||||
}
|
||||
|
||||
// 设置默认值
|
||||
if userLearning.LastStudyAt.IsZero() {
|
||||
userLearning.LastStudyAt = time.Now()
|
||||
}
|
||||
if userLearning.Status == "" {
|
||||
userLearning.Status = "learning"
|
||||
}
|
||||
|
||||
err = global.GVA_DB.WithContext(ctx).Create(userLearning).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteUserLearning 删除用户学习记录
|
||||
func (userLearningService *UserLearningService) DeleteUserLearning(ctx context.Context, ID string) (err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Delete(&learning.UserLearning{}, ID).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteUserLearningByIds 批量删除用户学习记录
|
||||
func (userLearningService *UserLearningService) DeleteUserLearningByIds(ctx context.Context, IDs []string) (err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Delete(&[]learning.UserLearning{}, "id in ?", IDs).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateUserLearning 更新用户学习记录
|
||||
func (userLearningService *UserLearningService) UpdateUserLearning(ctx context.Context, userLearning learning.UserLearning) (err error) {
|
||||
// 更新最后学习时间
|
||||
userLearning.LastStudyAt = time.Now()
|
||||
|
||||
// 如果进度达到100%,设置完成状态和完成时间
|
||||
if userLearning.Progress >= 100 && userLearning.Status != "completed" {
|
||||
userLearning.Status = "completed"
|
||||
userLearning.CompletedAt = time.Now()
|
||||
}
|
||||
|
||||
err = global.GVA_DB.WithContext(ctx).Model(&learning.UserLearning{}).Where("id = ?", userLearning.ID).Updates(&userLearning).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// GetUserLearning 根据ID获取用户学习记录
|
||||
func (userLearningService *UserLearningService) GetUserLearning(ctx context.Context, ID string) (userLearning learning.UserLearning, err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Where("id = ?", ID).First(&userLearning).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// 手动查询关联信息
|
||||
userLearningService.loadAssociations(ctx, &userLearning)
|
||||
return
|
||||
}
|
||||
|
||||
// GetUserLearningInfoList 分页获取用户学习记录列表
|
||||
func (userLearningService *UserLearningService) GetUserLearningInfoList(ctx context.Context, info learningReq.UserLearningSearch) (list []learning.UserLearning, total int64, err error) {
|
||||
limit := info.PageSize
|
||||
offset := info.PageSize * (info.Page - 1)
|
||||
// 创建db
|
||||
db := global.GVA_DB.WithContext(ctx).Model(&learning.UserLearning{})
|
||||
var userLearnings []learning.UserLearning
|
||||
// 如果有条件搜索 下方会自动创建搜索语句
|
||||
if info.StartCreatedAt != nil && info.EndCreatedAt != nil {
|
||||
db = db.Where("created_at BETWEEN ? AND ?", info.StartCreatedAt, info.EndCreatedAt)
|
||||
}
|
||||
if info.UserId != 0 {
|
||||
db = db.Where("user_id = ?", info.UserId)
|
||||
}
|
||||
if info.CourseId != 0 {
|
||||
db = db.Where("course_id = ?", info.CourseId)
|
||||
}
|
||||
if info.Status != "" {
|
||||
db = db.Where("status = ?", info.Status)
|
||||
}
|
||||
err = db.Count(&total).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if limit != 0 {
|
||||
db = db.Limit(limit).Offset(offset)
|
||||
}
|
||||
|
||||
err = db.Order("last_study_at desc").Find(&userLearnings).Error
|
||||
if err != nil {
|
||||
return userLearnings, total, err
|
||||
}
|
||||
|
||||
// 批量加载关联信息
|
||||
for i := range userLearnings {
|
||||
userLearningService.loadAssociations(ctx, &userLearnings[i])
|
||||
}
|
||||
|
||||
return userLearnings, total, err
|
||||
}
|
||||
|
||||
// GetUserProgress 获取用户学习进度
|
||||
func (userLearningService *UserLearningService) GetUserProgress(ctx context.Context, userId uint, courseId uint) (progress []learning.UserLearning, err error) {
|
||||
db := global.GVA_DB.WithContext(ctx).Where("user_id = ?", userId)
|
||||
if courseId != 0 {
|
||||
db = db.Where("course_id = ?", courseId)
|
||||
}
|
||||
err = db.Find(&progress).Error
|
||||
|
||||
// 批量加载关联信息
|
||||
for i := range progress {
|
||||
userLearningService.loadAssociations(ctx, &progress[i])
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GetLearningStatistics 获取用户学习统计
|
||||
func (userLearningService *UserLearningService) GetLearningStatistics(ctx context.Context, userId uint) (stats learningReq.LearningStatistics, err error) {
|
||||
// 总课程数
|
||||
var totalCourses int64
|
||||
err = global.GVA_DB.WithContext(ctx).Model(&learning.UserLearning{}).Where("user_id = ?", userId).Count(&totalCourses).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
stats.TotalCourses = int(totalCourses)
|
||||
|
||||
// 已完成课程数
|
||||
var completedCourses int64
|
||||
err = global.GVA_DB.WithContext(ctx).Model(&learning.UserLearning{}).Where("user_id = ? AND status = ?", userId, "completed").Count(&completedCourses).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
stats.CompletedCourses = int(completedCourses)
|
||||
|
||||
// 学习中课程数
|
||||
var inProgressCourses int64
|
||||
err = global.GVA_DB.WithContext(ctx).Model(&learning.UserLearning{}).Where("user_id = ? AND status = ?", userId, "learning").Count(&inProgressCourses).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
stats.InProgressCourses = int(inProgressCourses)
|
||||
|
||||
// 总学习时长和平均分数
|
||||
var result struct {
|
||||
TotalStudyTime int
|
||||
AverageScore int
|
||||
}
|
||||
err = global.GVA_DB.WithContext(ctx).Model(&learning.UserLearning{}).
|
||||
Select("SUM(study_time) as total_study_time, AVG(score) as average_score").
|
||||
Where("user_id = ?", userId).
|
||||
Scan(&result).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
stats.TotalStudyTime = result.TotalStudyTime
|
||||
stats.AverageScore = result.AverageScore
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// loadAssociations 加载关联信息
|
||||
func (userLearningService *UserLearningService) loadAssociations(ctx context.Context, userLearning *learning.UserLearning) {
|
||||
// 加载课程信息
|
||||
if userLearning.CourseId != 0 {
|
||||
var course learning.Course
|
||||
if err := global.GVA_DB.WithContext(ctx).Where("id = ?", userLearning.CourseId).First(&course).Error; err == nil {
|
||||
userLearning.Course = course
|
||||
}
|
||||
}
|
||||
|
||||
// 加载章节信息
|
||||
if userLearning.ChapterId != 0 {
|
||||
var chapter learning.Chapter
|
||||
if err := global.GVA_DB.WithContext(ctx).Where("id = ?", userLearning.ChapterId).First(&chapter).Error; err == nil {
|
||||
userLearning.Chapter = chapter
|
||||
}
|
||||
}
|
||||
|
||||
// 加载知识点信息
|
||||
if userLearning.KnowledgePointId != 0 {
|
||||
var knowledgePoint learning.KnowledgePoint
|
||||
if err := global.GVA_DB.WithContext(ctx).Where("id = ?", userLearning.KnowledgePointId).First(&knowledgePoint).Error; err == nil {
|
||||
userLearning.KnowledgePoint = knowledgePoint
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
package learning
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/global"
|
||||
"github.com/flipped-aurora/gin-vue-admin/server/model/learning"
|
||||
learningReq "github.com/flipped-aurora/gin-vue-admin/server/model/learning/request"
|
||||
)
|
||||
|
||||
type WrongQuestionService struct{}
|
||||
|
||||
// CreateWrongQuestion 创建错题记录
|
||||
func (wrongQuestionService *WrongQuestionService) CreateWrongQuestion(ctx context.Context, wrongQuestion *learning.WrongQuestion) (err error) {
|
||||
// 检查是否已存在该用户对该题目的错题记录
|
||||
var existingRecord learning.WrongQuestion
|
||||
err = global.GVA_DB.WithContext(ctx).Where("user_id = ? AND question_id = ?", wrongQuestion.UserId, wrongQuestion.QuestionId).First(&existingRecord).Error
|
||||
if err == nil {
|
||||
// 如果已存在,更新错误次数和最后错误时间
|
||||
existingRecord.WrongCount++
|
||||
existingRecord.LastWrongAt = time.Now()
|
||||
existingRecord.WrongAnswer = wrongQuestion.WrongAnswer
|
||||
existingRecord.IsMastered = false // 重新答错,标记为未掌握
|
||||
existingRecord.MasteredAt = time.Time{}
|
||||
return wrongQuestionService.UpdateWrongQuestion(ctx, existingRecord)
|
||||
}
|
||||
|
||||
// 设置默认值
|
||||
if wrongQuestion.LastWrongAt.IsZero() {
|
||||
wrongQuestion.LastWrongAt = time.Now()
|
||||
}
|
||||
if wrongQuestion.WrongCount == 0 {
|
||||
wrongQuestion.WrongCount = 1
|
||||
}
|
||||
|
||||
err = global.GVA_DB.WithContext(ctx).Create(wrongQuestion).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteWrongQuestion 删除错题记录
|
||||
func (wrongQuestionService *WrongQuestionService) DeleteWrongQuestion(ctx context.Context, ID string) (err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Delete(&learning.WrongQuestion{}, ID).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteWrongQuestionByIds 批量删除错题记录
|
||||
func (wrongQuestionService *WrongQuestionService) DeleteWrongQuestionByIds(ctx context.Context, IDs []string) (err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Delete(&[]learning.WrongQuestion{}, "id in ?", IDs).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateWrongQuestion 更新错题记录
|
||||
func (wrongQuestionService *WrongQuestionService) UpdateWrongQuestion(ctx context.Context, wrongQuestion learning.WrongQuestion) (err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Model(&learning.WrongQuestion{}).Where("id = ?", wrongQuestion.ID).Updates(&wrongQuestion).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// GetWrongQuestion 根据ID获取错题记录
|
||||
func (wrongQuestionService *WrongQuestionService) GetWrongQuestion(ctx context.Context, ID string) (wrongQuestion learning.WrongQuestion, err error) {
|
||||
err = global.GVA_DB.WithContext(ctx).Where("id = ?", ID).First(&wrongQuestion).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// 手动查询关联的题目信息
|
||||
wrongQuestionService.loadQuestionInfo(ctx, &wrongQuestion)
|
||||
return
|
||||
}
|
||||
|
||||
// GetWrongQuestionInfoList 分页获取错题记录列表
|
||||
func (wrongQuestionService *WrongQuestionService) GetWrongQuestionInfoList(ctx context.Context, info learningReq.WrongQuestionSearch) (list []learning.WrongQuestion, total int64, err error) {
|
||||
limit := info.PageSize
|
||||
offset := info.PageSize * (info.Page - 1)
|
||||
// 创建db
|
||||
db := global.GVA_DB.WithContext(ctx).Model(&learning.WrongQuestion{})
|
||||
var wrongQuestions []learning.WrongQuestion
|
||||
// 如果有条件搜索 下方会自动创建搜索语句
|
||||
if info.StartCreatedAt != nil && info.EndCreatedAt != nil {
|
||||
db = db.Where("created_at BETWEEN ? AND ?", info.StartCreatedAt, info.EndCreatedAt)
|
||||
}
|
||||
if info.UserId != 0 {
|
||||
db = db.Where("user_id = ?", info.UserId)
|
||||
}
|
||||
if info.QuestionId != 0 {
|
||||
db = db.Where("question_id = ?", info.QuestionId)
|
||||
}
|
||||
if info.IsMastered {
|
||||
db = db.Where("is_mastered = ?", info.IsMastered)
|
||||
}
|
||||
err = db.Count(&total).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if limit != 0 {
|
||||
db = db.Limit(limit).Offset(offset)
|
||||
}
|
||||
|
||||
err = db.Order("last_wrong_at desc").Find(&wrongQuestions).Error
|
||||
if err != nil {
|
||||
return wrongQuestions, total, err
|
||||
}
|
||||
|
||||
// 批量加载关联的题目信息
|
||||
for i := range wrongQuestions {
|
||||
wrongQuestionService.loadQuestionInfo(ctx, &wrongQuestions[i])
|
||||
}
|
||||
|
||||
return wrongQuestions, total, err
|
||||
}
|
||||
|
||||
// GetUserWrongQuestions 获取用户的错题列表
|
||||
func (wrongQuestionService *WrongQuestionService) GetUserWrongQuestions(ctx context.Context, userId uint, onlyUnmastered bool) (list []learning.WrongQuestion, err error) {
|
||||
db := global.GVA_DB.WithContext(ctx).Where("user_id = ?", userId)
|
||||
if onlyUnmastered {
|
||||
db = db.Where("is_mastered = ?", false)
|
||||
}
|
||||
err = db.Order("last_wrong_at desc").Find(&list).Error
|
||||
|
||||
// 批量加载关联的题目信息
|
||||
for i := range list {
|
||||
wrongQuestionService.loadQuestionInfo(ctx, &list[i])
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// MarkAsMastered 标记题目为已掌握
|
||||
func (wrongQuestionService *WrongQuestionService) MarkAsMastered(ctx context.Context, userId uint, wrongQuestionId uint) (err error) {
|
||||
updates := map[string]interface{}{
|
||||
"is_mastered": true,
|
||||
"mastered_at": time.Now(),
|
||||
}
|
||||
err = global.GVA_DB.WithContext(ctx).Model(&learning.WrongQuestion{}).
|
||||
Where("id = ? AND user_id = ?", wrongQuestionId, userId).
|
||||
Updates(updates).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// GetWrongQuestionStatistics 获取用户错题统计
|
||||
func (wrongQuestionService *WrongQuestionService) GetWrongQuestionStatistics(ctx context.Context, userId uint) (stats learningReq.WrongQuestionStatistics, err error) {
|
||||
// 总错题数
|
||||
var totalWrongQuestions int64
|
||||
err = global.GVA_DB.WithContext(ctx).Model(&learning.WrongQuestion{}).Where("user_id = ?", userId).Count(&totalWrongQuestions).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
stats.TotalWrongQuestions = int(totalWrongQuestions)
|
||||
|
||||
// 已掌握题目数
|
||||
var masteredQuestions int64
|
||||
err = global.GVA_DB.WithContext(ctx).Model(&learning.WrongQuestion{}).Where("user_id = ? AND is_mastered = ?", userId, true).Count(&masteredQuestions).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
stats.MasteredQuestions = int(masteredQuestions)
|
||||
|
||||
// 未掌握题目数
|
||||
stats.UnmasteredQuestions = stats.TotalWrongQuestions - stats.MasteredQuestions
|
||||
|
||||
// 平均错误次数
|
||||
var result struct {
|
||||
AverageWrongCount float64
|
||||
}
|
||||
err = global.GVA_DB.WithContext(ctx).Model(&learning.WrongQuestion{}).
|
||||
Select("AVG(wrong_count) as average_wrong_count").
|
||||
Where("user_id = ?", userId).
|
||||
Scan(&result).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
stats.AverageWrongCount = int(result.AverageWrongCount)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// loadQuestionInfo 加载题目信息
|
||||
func (wrongQuestionService *WrongQuestionService) loadQuestionInfo(ctx context.Context, wrongQuestion *learning.WrongQuestion) {
|
||||
if wrongQuestion.QuestionId != 0 {
|
||||
var question learning.Question
|
||||
if err := global.GVA_DB.WithContext(ctx).Where("id = ?", wrongQuestion.QuestionId).First(&question).Error; err == nil {
|
||||
wrongQuestion.Question = question
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -197,6 +197,52 @@ func (i *initApi) InitializeData(ctx context.Context) (context.Context, error) {
|
||||
{ApiGroup: "版本控制", Method: "POST", Path: "/sysVersion/importVersion", Description: "同步版本"},
|
||||
{ApiGroup: "版本控制", Method: "DELETE", Path: "/sysVersion/deleteSysVersion", Description: "删除版本"},
|
||||
{ApiGroup: "版本控制", Method: "DELETE", Path: "/sysVersion/deleteSysVersionByIds", Description: "批量删除版本"},
|
||||
|
||||
// 学习系统 - 课程管理
|
||||
{ApiGroup: "课程管理", Method: "POST", Path: "/course/createCourse", Description: "创建课程"},
|
||||
{ApiGroup: "课程管理", Method: "DELETE", Path: "/course/deleteCourse", Description: "删除课程"},
|
||||
{ApiGroup: "课程管理", Method: "DELETE", Path: "/course/deleteCourseByIds", Description: "批量删除课程"},
|
||||
{ApiGroup: "课程管理", Method: "PUT", Path: "/course/updateCourse", Description: "更新课程"},
|
||||
{ApiGroup: "课程管理", Method: "GET", Path: "/course/findCourse", Description: "根据ID获取课程"},
|
||||
{ApiGroup: "课程管理", Method: "GET", Path: "/course/getCourseList", Description: "获取课程列表"},
|
||||
{ApiGroup: "课程管理", Method: "GET", Path: "/course/getPublishedCourses", Description: "获取已发布课程列表"},
|
||||
|
||||
// 学习系统 - 章节管理
|
||||
{ApiGroup: "章节管理", Method: "POST", Path: "/chapter/createChapter", Description: "创建章节"},
|
||||
{ApiGroup: "章节管理", Method: "DELETE", Path: "/chapter/deleteChapter", Description: "删除章节"},
|
||||
{ApiGroup: "章节管理", Method: "DELETE", Path: "/chapter/deleteChapterByIds", Description: "批量删除章节"},
|
||||
{ApiGroup: "章节管理", Method: "PUT", Path: "/chapter/updateChapter", Description: "更新章节"},
|
||||
{ApiGroup: "章节管理", Method: "GET", Path: "/chapter/findChapter", Description: "根据ID获取章节"},
|
||||
{ApiGroup: "章节管理", Method: "GET", Path: "/chapter/getChapterList", Description: "获取章节列表"},
|
||||
{ApiGroup: "章节管理", Method: "GET", Path: "/chapter/getChaptersByCourse", Description: "根据课程获取章节列表"},
|
||||
|
||||
// 学习系统 - 知识点管理
|
||||
{ApiGroup: "知识点管理", Method: "POST", Path: "/knowledgePoint/createKnowledgePoint", Description: "创建知识点"},
|
||||
{ApiGroup: "知识点管理", Method: "DELETE", Path: "/knowledgePoint/deleteKnowledgePoint", Description: "删除知识点"},
|
||||
{ApiGroup: "知识点管理", Method: "DELETE", Path: "/knowledgePoint/deleteKnowledgePointByIds", Description: "批量删除知识点"},
|
||||
{ApiGroup: "知识点管理", Method: "PUT", Path: "/knowledgePoint/updateKnowledgePoint", Description: "更新知识点"},
|
||||
{ApiGroup: "知识点管理", Method: "GET", Path: "/knowledgePoint/findKnowledgePoint", Description: "根据ID获取知识点"},
|
||||
{ApiGroup: "知识点管理", Method: "GET", Path: "/knowledgePoint/getKnowledgePointList", Description: "获取知识点列表"},
|
||||
{ApiGroup: "知识点管理", Method: "GET", Path: "/knowledgePoint/getKnowledgePointsByChapter", Description: "根据章节获取知识点列表"},
|
||||
|
||||
// 学习系统 - 题目管理
|
||||
{ApiGroup: "题目管理", Method: "POST", Path: "/question/createQuestion", Description: "创建题目"},
|
||||
{ApiGroup: "题目管理", Method: "DELETE", Path: "/question/deleteQuestion", Description: "删除题目"},
|
||||
{ApiGroup: "题目管理", Method: "DELETE", Path: "/question/deleteQuestionByIds", Description: "批量删除题目"},
|
||||
{ApiGroup: "题目管理", Method: "PUT", Path: "/question/updateQuestion", Description: "更新题目"},
|
||||
{ApiGroup: "题目管理", Method: "GET", Path: "/question/findQuestion", Description: "根据ID获取题目"},
|
||||
{ApiGroup: "题目管理", Method: "GET", Path: "/question/getQuestionList", Description: "获取题目列表"},
|
||||
{ApiGroup: "题目管理", Method: "GET", Path: "/question/getQuestionsByKnowledgePoint", Description: "根据知识点获取题目列表"},
|
||||
|
||||
// 学习系统 - 学习记录管理
|
||||
{ApiGroup: "学习记录管理", Method: "POST", Path: "/userLearning/createUserLearning", Description: "创建学习记录"},
|
||||
{ApiGroup: "学习记录管理", Method: "DELETE", Path: "/userLearning/deleteUserLearning", Description: "删除学习记录"},
|
||||
{ApiGroup: "学习记录管理", Method: "DELETE", Path: "/userLearning/deleteUserLearningByIds", Description: "批量删除学习记录"},
|
||||
{ApiGroup: "学习记录管理", Method: "PUT", Path: "/userLearning/updateUserLearning", Description: "更新学习记录"},
|
||||
{ApiGroup: "学习记录管理", Method: "GET", Path: "/userLearning/findUserLearning", Description: "根据ID获取学习记录"},
|
||||
{ApiGroup: "学习记录管理", Method: "GET", Path: "/userLearning/getUserLearningList", Description: "获取学习记录列表"},
|
||||
{ApiGroup: "学习记录管理", Method: "GET", Path: "/userLearning/getUserProgress", Description: "获取用户学习进度"},
|
||||
{ApiGroup: "学习记录管理", Method: "GET", Path: "/userLearning/getLearningStatistics", Description: "获取用户学习统计"},
|
||||
}
|
||||
if err := db.Create(&entities).Error; err != nil {
|
||||
return ctx, errors.Wrap(err, sysModel.SysApi{}.TableName()+"表数据初始化失败!")
|
||||
|
||||
@@ -50,6 +50,16 @@ func (i *initDict) InitializeData(ctx context.Context) (next context.Context, er
|
||||
{Name: "数据库浮点型", Type: "float64", Status: &True, Desc: "数据库浮点型"},
|
||||
{Name: "数据库字符串", Type: "string", Status: &True, Desc: "数据库字符串"},
|
||||
{Name: "数据库bool类型", Type: "bool", Status: &True, Desc: "数据库bool类型"},
|
||||
|
||||
// 学习系统字典
|
||||
{Name: "课程难度", Type: "course_difficulty", Status: &True, Desc: "课程难度等级字典"},
|
||||
{Name: "课程状态", Type: "course_status", Status: &True, Desc: "课程状态字典"},
|
||||
{Name: "章节状态", Type: "chapter_status", Status: &True, Desc: "章节状态字典"},
|
||||
{Name: "知识点状态", Type: "knowledge_status", Status: &True, Desc: "知识点状态字典"},
|
||||
{Name: "题目类型", Type: "question_type", Status: &True, Desc: "题目类型字典"},
|
||||
{Name: "题目难度", Type: "question_difficulty", Status: &True, Desc: "题目难度等级字典"},
|
||||
{Name: "学习状态", Type: "learning_status", Status: &True, Desc: "用户学习状态字典"},
|
||||
{Name: "考试状态", Type: "exam_status", Status: &True, Desc: "考试状态字典"},
|
||||
}
|
||||
|
||||
if err = db.Create(&entities).Error; err != nil {
|
||||
|
||||
@@ -98,6 +98,65 @@ func (i *initDictDetail) InitializeData(ctx context.Context) (context.Context, e
|
||||
{Label: "tinyint", Value: "1", Extend: "mysql", Status: &True},
|
||||
{Label: "bool", Value: "2", Extend: "pgsql", Status: &True},
|
||||
}
|
||||
|
||||
// 课程难度字典详情
|
||||
dicts[6].SysDictionaryDetails = []sysModel.SysDictionaryDetail{
|
||||
{Label: "初级", Value: "beginner", Status: &True, Sort: 1},
|
||||
{Label: "中级", Value: "intermediate", Status: &True, Sort: 2},
|
||||
{Label: "高级", Value: "advanced", Status: &True, Sort: 3},
|
||||
}
|
||||
|
||||
// 课程状态字典详情
|
||||
dicts[7].SysDictionaryDetails = []sysModel.SysDictionaryDetail{
|
||||
{Label: "草稿", Value: "draft", Status: &True, Sort: 1},
|
||||
{Label: "已发布", Value: "published", Status: &True, Sort: 2},
|
||||
{Label: "已下架", Value: "archived", Status: &True, Sort: 3},
|
||||
}
|
||||
|
||||
// 章节状态字典详情
|
||||
dicts[8].SysDictionaryDetails = []sysModel.SysDictionaryDetail{
|
||||
{Label: "草稿", Value: "draft", Status: &True, Sort: 1},
|
||||
{Label: "已发布", Value: "published", Status: &True, Sort: 2},
|
||||
{Label: "隐藏", Value: "hidden", Status: &True, Sort: 3},
|
||||
}
|
||||
|
||||
// 知识点状态字典详情
|
||||
dicts[9].SysDictionaryDetails = []sysModel.SysDictionaryDetail{
|
||||
{Label: "草稿", Value: "draft", Status: &True, Sort: 1},
|
||||
{Label: "已发布", Value: "published", Status: &True, Sort: 2},
|
||||
{Label: "隐藏", Value: "hidden", Status: &True, Sort: 3},
|
||||
}
|
||||
|
||||
// 题目类型字典详情
|
||||
dicts[10].SysDictionaryDetails = []sysModel.SysDictionaryDetail{
|
||||
{Label: "单选题", Value: "single_choice", Status: &True, Sort: 1},
|
||||
{Label: "多选题", Value: "multiple_choice", Status: &True, Sort: 2},
|
||||
{Label: "判断题", Value: "true_false", Status: &True, Sort: 3},
|
||||
{Label: "填空题", Value: "fill_blank", Status: &True, Sort: 4},
|
||||
}
|
||||
|
||||
// 题目难度字典详情
|
||||
dicts[11].SysDictionaryDetails = []sysModel.SysDictionaryDetail{
|
||||
{Label: "简单", Value: "easy", Status: &True, Sort: 1},
|
||||
{Label: "中等", Value: "medium", Status: &True, Sort: 2},
|
||||
{Label: "困难", Value: "hard", Status: &True, Sort: 3},
|
||||
}
|
||||
|
||||
// 学习状态字典详情
|
||||
dicts[12].SysDictionaryDetails = []sysModel.SysDictionaryDetail{
|
||||
{Label: "未开始", Value: "not_started", Status: &True, Sort: 1},
|
||||
{Label: "学习中", Value: "learning", Status: &True, Sort: 2},
|
||||
{Label: "已完成", Value: "completed", Status: &True, Sort: 3},
|
||||
{Label: "已暂停", Value: "paused", Status: &True, Sort: 4},
|
||||
}
|
||||
|
||||
// 考试状态字典详情
|
||||
dicts[13].SysDictionaryDetails = []sysModel.SysDictionaryDetail{
|
||||
{Label: "草稿", Value: "draft", Status: &True, Sort: 1},
|
||||
{Label: "已发布", Value: "published", Status: &True, Sort: 2},
|
||||
{Label: "已结束", Value: "ended", Status: &True, Sort: 3},
|
||||
{Label: "已取消", Value: "cancelled", Status: &True, Sort: 4},
|
||||
}
|
||||
for _, dict := range dicts {
|
||||
if err := db.Model(&dict).Association("SysDictionaryDetails").
|
||||
Replace(dict.SysDictionaryDetails); err != nil {
|
||||
|
||||
@@ -62,6 +62,8 @@ func (i *initMenu) InitializeData(ctx context.Context) (next context.Context, er
|
||||
{MenuLevel: 0, Hidden: false, ParentId: 0, Path: "https://www.gin-vue-admin.com", Name: "https://www.gin-vue-admin.com", Component: "/", Sort: 0, Meta: Meta{Title: "官方网站", Icon: "customer-gva"}},
|
||||
{MenuLevel: 0, Hidden: false, ParentId: 0, Path: "state", Name: "state", Component: "view/system/state.vue", Sort: 8, Meta: Meta{Title: "服务器状态", Icon: "cloudy"}},
|
||||
{MenuLevel: 0, Hidden: false, ParentId: 0, Path: "plugin", Name: "plugin", Component: "view/routerHolder.vue", Sort: 6, Meta: Meta{Title: "插件系统", Icon: "cherry"}},
|
||||
{MenuLevel: 0, Hidden: false, ParentId: 0, Path: "learning", Name: "learning", Component: "view/routerHolder.vue", Sort: 3, Meta: Meta{Title: "教学管理", Icon: "management"}},
|
||||
{MenuLevel: 0, Hidden: false, ParentId: 0, Path: "student", Name: "student", Component: "view/routerHolder.vue", Sort: 2, Meta: Meta{Title: "在线学习", Icon: "reading"}},
|
||||
}
|
||||
|
||||
// 先创建父级菜单(ParentId = 0 的菜单)
|
||||
@@ -109,6 +111,19 @@ func (i *initMenu) InitializeData(ctx context.Context) (next context.Context, er
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["plugin"], Path: "pubPlug", Name: "pubPlug", Component: "view/systemTools/pubPlug/pubPlug.vue", Sort: 3, Meta: Meta{Title: "打包插件", Icon: "files"}},
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["plugin"], Path: "plugin-email", Name: "plugin-email", Component: "plugin/email/view/index.vue", Sort: 4, Meta: Meta{Title: "邮件插件", Icon: "message"}},
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["plugin"], Path: "anInfo", Name: "anInfo", Component: "plugin/announcement/view/info.vue", Sort: 5, Meta: Meta{Title: "公告管理[示例]", Icon: "scaleToOriginal"}},
|
||||
|
||||
// learning子菜单(教学管理)
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["learning"], Path: "course", Name: "course", Component: "view/learning/admin/course/course.vue", Sort: 1, Meta: Meta{Title: "课程管理", Icon: "notebook", KeepAlive: true}},
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["learning"], Path: "chapter", Name: "chapter", Component: "view/learning/admin/chapter/chapter.vue", Sort: 2, Meta: Meta{Title: "章节管理", Icon: "collection", KeepAlive: true}},
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["learning"], Path: "knowledgePoint", Name: "knowledgePoint", Component: "view/learning/admin/knowledgePoint/knowledgePoint.vue", Sort: 3, Meta: Meta{Title: "知识点管理", Icon: "document", KeepAlive: true}},
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["learning"], Path: "question", Name: "question", Component: "view/learning/admin/question/question.vue", Sort: 4, Meta: Meta{Title: "题目管理", Icon: "edit", KeepAlive: true}},
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["learning"], Path: "learningRecords", Name: "learningRecords", Component: "view/learning/admin/records/learningRecords.vue", Sort: 5, Meta: Meta{Title: "学习记录", Icon: "histogram", KeepAlive: true}},
|
||||
|
||||
// student子菜单(在线学习)
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["student"], Path: "dashboard", Name: "dashboard", Component: "view/learning/student/dashboard/dashboard.vue", Sort: 1, Meta: Meta{Title: "学习首页", Icon: "house", KeepAlive: true}},
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["student"], Path: "myCourses", Name: "myCourses", Component: "view/learning/student/courses/myCourses.vue", Sort: 2, Meta: Meta{Title: "我的课程", Icon: "notebook", KeepAlive: true}},
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["student"], Path: "onlineExam", Name: "onlineExam", Component: "view/learning/student/exam/onlineExam.vue", Sort: 3, Meta: Meta{Title: "在线考试", Icon: "medal", KeepAlive: true}},
|
||||
{MenuLevel: 1, Hidden: false, ParentId: menuNameMap["student"], Path: "wrongQuestions", Name: "wrongQuestions", Component: "view/learning/student/wrongQuestion/wrongQuestions.vue", Sort: 4, Meta: Meta{Title: "我的错题", Icon: "warning", KeepAlive: true}},
|
||||
}
|
||||
|
||||
// 创建子菜单
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
-- =============================================
|
||||
-- 学习系统完整初始化SQL脚本
|
||||
-- 包含所有必要的数据字典、菜单权限、API权限配置
|
||||
-- 执行顺序:先执行此文件,再根据需要执行角色权限配置
|
||||
-- =============================================
|
||||
|
||||
-- 1. 插入学习系统数据字典
|
||||
INSERT INTO `sys_dictionaries` (`name`, `type`, `status`, `desc`) VALUES
|
||||
('课程难度', 'course_difficulty', 1, '课程难度等级字典'),
|
||||
('课程状态', 'course_status', 1, '课程状态字典'),
|
||||
('章节状态', 'chapter_status', 1, '章节状态字典'),
|
||||
('知识点状态', 'knowledge_status', 1, '知识点状态字典'),
|
||||
('题目类型', 'question_type', 1, '题目类型字典'),
|
||||
('题目难度', 'question_difficulty', 1, '题目难度等级字典'),
|
||||
('学习状态', 'learning_status', 1, '用户学习状态字典'),
|
||||
('考试状态', 'exam_status', 1, '考试状态字典');
|
||||
|
||||
-- 2. 获取字典ID并插入字典详情
|
||||
SET @course_difficulty_id = (SELECT id FROM sys_dictionaries WHERE type = 'course_difficulty');
|
||||
SET @course_status_id = (SELECT id FROM sys_dictionaries WHERE type = 'course_status');
|
||||
SET @chapter_status_id = (SELECT id FROM sys_dictionaries WHERE type = 'chapter_status');
|
||||
SET @knowledge_status_id = (SELECT id FROM sys_dictionaries WHERE type = 'knowledge_status');
|
||||
SET @question_type_id = (SELECT id FROM sys_dictionaries WHERE type = 'question_type');
|
||||
SET @question_difficulty_id = (SELECT id FROM sys_dictionaries WHERE type = 'question_difficulty');
|
||||
SET @learning_status_id = (SELECT id FROM sys_dictionaries WHERE type = 'learning_status');
|
||||
SET @exam_status_id = (SELECT id FROM sys_dictionaries WHERE type = 'exam_status');
|
||||
|
||||
-- 3. 插入课程难度字典详情
|
||||
INSERT INTO `sys_dictionary_details` (`label`, `value`, `status`, `sort`, `sys_dictionary_id`) VALUES
|
||||
('初级', 'beginner', 1, 1, @course_difficulty_id),
|
||||
('中级', 'intermediate', 1, 2, @course_difficulty_id),
|
||||
('高级', 'advanced', 1, 3, @course_difficulty_id);
|
||||
|
||||
-- 4. 插入课程状态字典详情
|
||||
INSERT INTO `sys_dictionary_details` (`label`, `value`, `status`, `sort`, `sys_dictionary_id`) VALUES
|
||||
('草稿', 'draft', 1, 1, @course_status_id),
|
||||
('已发布', 'published', 1, 2, @course_status_id),
|
||||
('已下架', 'archived', 1, 3, @course_status_id);
|
||||
|
||||
-- 5. 插入章节状态字典详情
|
||||
INSERT INTO `sys_dictionary_details` (`label`, `value`, `status`, `sort`, `sys_dictionary_id`) VALUES
|
||||
('草稿', 'draft', 1, 1, @chapter_status_id),
|
||||
('已发布', 'published', 1, 2, @chapter_status_id),
|
||||
('隐藏', 'hidden', 1, 3, @chapter_status_id);
|
||||
|
||||
-- 6. 插入知识点状态字典详情
|
||||
INSERT INTO `sys_dictionary_details` (`label`, `value`, `status`, `sort`, `sys_dictionary_id`) VALUES
|
||||
('草稿', 'draft', 1, 1, @knowledge_status_id),
|
||||
('已发布', 'published', 1, 2, @knowledge_status_id),
|
||||
('隐藏', 'hidden', 1, 3, @knowledge_status_id);
|
||||
|
||||
-- 7. 插入题目类型字典详情
|
||||
INSERT INTO `sys_dictionary_details` (`label`, `value`, `status`, `sort`, `sys_dictionary_id`) VALUES
|
||||
('单选题', 'single_choice', 1, 1, @question_type_id),
|
||||
('多选题', 'multiple_choice', 1, 2, @question_type_id),
|
||||
('判断题', 'true_false', 1, 3, @question_type_id),
|
||||
('填空题', 'fill_blank', 1, 4, @question_type_id);
|
||||
|
||||
-- 8. 插入题目难度字典详情
|
||||
INSERT INTO `sys_dictionary_details` (`label`, `value`, `status`, `sort`, `sys_dictionary_id`) VALUES
|
||||
('简单', 'easy', 1, 1, @question_difficulty_id),
|
||||
('中等', 'medium', 1, 2, @question_difficulty_id),
|
||||
('困难', 'hard', 1, 3, @question_difficulty_id);
|
||||
|
||||
-- 9. 插入学习状态字典详情
|
||||
INSERT INTO `sys_dictionary_details` (`label`, `value`, `status`, `sort`, `sys_dictionary_id`) VALUES
|
||||
('未开始', 'not_started', 1, 1, @learning_status_id),
|
||||
('学习中', 'learning', 1, 2, @learning_status_id),
|
||||
('已完成', 'completed', 1, 3, @learning_status_id),
|
||||
('已暂停', 'paused', 1, 4, @learning_status_id);
|
||||
|
||||
-- 10. 插入考试状态字典详情
|
||||
INSERT INTO `sys_dictionary_details` (`label`, `value`, `status`, `sort`, `sys_dictionary_id`) VALUES
|
||||
('草稿', 'draft', 1, 1, @exam_status_id),
|
||||
('已发布', 'published', 1, 2, @exam_status_id),
|
||||
('已结束', 'ended', 1, 3, @exam_status_id),
|
||||
('已取消', 'cancelled', 1, 4, @exam_status_id);
|
||||
|
||||
-- =============================================
|
||||
-- 菜单权限配置
|
||||
-- =============================================
|
||||
|
||||
-- 11. 插入主菜单
|
||||
INSERT INTO `sys_base_menus` (`menu_level`, `parent_id`, `path`, `name`, `hidden`, `component`, `sort`, `title`, `icon`, `keep_alive`) VALUES
|
||||
(0, 0, 'learning', 'learning', 0, 'view/routerHolder.vue', 3, '教学管理', 'management', 0),
|
||||
(0, 0, 'student', 'student', 0, 'view/routerHolder.vue', 2, '在线学习', 'reading', 0);
|
||||
|
||||
-- 12. 获取主菜单ID
|
||||
SET @learning_menu_id = (SELECT id FROM sys_base_menus WHERE name = 'learning' AND menu_level = 0);
|
||||
SET @student_menu_id = (SELECT id FROM sys_base_menus WHERE name = 'student' AND menu_level = 0);
|
||||
|
||||
-- 13. 插入教学管理子菜单
|
||||
INSERT INTO `sys_base_menus` (`menu_level`, `parent_id`, `path`, `name`, `hidden`, `component`, `sort`, `title`, `icon`, `keep_alive`) VALUES
|
||||
(1, @learning_menu_id, 'course', 'course', 0, 'view/learning/admin/course/course.vue', 1, '课程管理', 'notebook', 1),
|
||||
(1, @learning_menu_id, 'chapter', 'chapter', 0, 'view/learning/admin/chapter/chapter.vue', 2, '章节管理', 'collection', 1),
|
||||
(1, @learning_menu_id, 'knowledgePoint', 'knowledgePoint', 0, 'view/learning/admin/knowledgePoint/knowledgePoint.vue', 3, '知识点管理', 'document', 1),
|
||||
(1, @learning_menu_id, 'question', 'question', 0, 'view/learning/admin/question/question.vue', 4, '题目管理', 'edit', 1),
|
||||
(1, @learning_menu_id, 'exam', 'exam', 0, 'view/learning/admin/exam/exam.vue', 5, '考试管理', 'medal', 1),
|
||||
(1, @learning_menu_id, 'learningRecords', 'learningRecords', 0, 'view/learning/admin/records/learningRecords.vue', 6, '学习记录', 'histogram', 1);
|
||||
|
||||
-- 14. 插入在线学习子菜单
|
||||
INSERT INTO `sys_base_menus` (`menu_level`, `parent_id`, `path`, `name`, `hidden`, `component`, `sort`, `title`, `icon`, `keep_alive`) VALUES
|
||||
(1, @student_menu_id, 'dashboard', 'dashboard', 0, 'view/learning/student/dashboard/dashboard.vue', 1, '学习首页', 'house', 1),
|
||||
(1, @student_menu_id, 'myCourses', 'myCourses', 0, 'view/learning/student/courses/myCourses.vue', 2, '我的课程', 'notebook', 1),
|
||||
(1, @student_menu_id, 'onlineExam', 'onlineExam', 0, 'view/learning/student/exam/onlineExam.vue', 3, '在线考试', 'medal', 1),
|
||||
(1, @student_menu_id, 'wrongQuestions', 'wrongQuestions', 0, 'view/learning/student/wrongQuestion/wrongQuestions.vue', 4, '我的错题', 'warning', 1);
|
||||
|
||||
-- =============================================
|
||||
-- API权限配置
|
||||
-- =============================================
|
||||
|
||||
-- 15. 插入学习系统API权限
|
||||
INSERT INTO `sys_apis` (`path`, `description`, `api_group`, `method`) VALUES
|
||||
-- 课程管理API
|
||||
('/course/createCourse', '创建课程', '课程管理', 'POST'),
|
||||
('/course/deleteCourse', '删除课程', '课程管理', 'DELETE'),
|
||||
('/course/deleteCourseByIds', '批量删除课程', '课程管理', 'DELETE'),
|
||||
('/course/updateCourse', '更新课程', '课程管理', 'PUT'),
|
||||
('/course/findCourse', '根据ID获取课程', '课程管理', 'GET'),
|
||||
('/course/getCourseList', '获取课程列表', '课程管理', 'GET'),
|
||||
('/course/getPublishedCourses', '获取已发布课程列表', '课程管理', 'GET'),
|
||||
|
||||
-- 章节管理API
|
||||
('/chapter/createChapter', '创建章节', '章节管理', 'POST'),
|
||||
('/chapter/deleteChapter', '删除章节', '章节管理', 'DELETE'),
|
||||
('/chapter/deleteChapterByIds', '批量删除章节', '章节管理', 'DELETE'),
|
||||
('/chapter/updateChapter', '更新章节', '章节管理', 'PUT'),
|
||||
('/chapter/findChapter', '根据ID获取章节', '章节管理', 'GET'),
|
||||
('/chapter/getChapterList', '获取章节列表', '章节管理', 'GET'),
|
||||
('/chapter/getChaptersByCourse', '根据课程获取章节列表', '章节管理', 'GET'),
|
||||
|
||||
-- 知识点管理API
|
||||
('/knowledgePoint/createKnowledgePoint', '创建知识点', '知识点管理', 'POST'),
|
||||
('/knowledgePoint/deleteKnowledgePoint', '删除知识点', '知识点管理', 'DELETE'),
|
||||
('/knowledgePoint/deleteKnowledgePointByIds', '批量删除知识点', '知识点管理', 'DELETE'),
|
||||
('/knowledgePoint/updateKnowledgePoint', '更新知识点', '知识点管理', 'PUT'),
|
||||
('/knowledgePoint/findKnowledgePoint', '根据ID获取知识点', '知识点管理', 'GET'),
|
||||
('/knowledgePoint/getKnowledgePointList', '获取知识点列表', '知识点管理', 'GET'),
|
||||
('/knowledgePoint/getKnowledgePointsByChapter', '根据章节获取知识点列表', '知识点管理', 'GET'),
|
||||
|
||||
-- 题目管理API
|
||||
('/question/createQuestion', '创建题目', '题目管理', 'POST'),
|
||||
('/question/deleteQuestion', '删除题目', '题目管理', 'DELETE'),
|
||||
('/question/deleteQuestionByIds', '批量删除题目', '题目管理', 'DELETE'),
|
||||
('/question/updateQuestion', '更新题目', '题目管理', 'PUT'),
|
||||
('/question/findQuestion', '根据ID获取题目', '题目管理', 'GET'),
|
||||
('/question/getQuestionList', '获取题目列表', '题目管理', 'GET'),
|
||||
('/question/getQuestionsByKnowledgePoint', '根据知识点获取题目列表', '题目管理', 'GET'),
|
||||
|
||||
-- 考试管理API
|
||||
('/exam/createExam', '创建考试', '考试管理', 'POST'),
|
||||
('/exam/deleteExam', '删除考试', '考试管理', 'DELETE'),
|
||||
('/exam/deleteExamByIds', '批量删除考试', '考试管理', 'DELETE'),
|
||||
('/exam/updateExam', '更新考试', '考试管理', 'PUT'),
|
||||
('/exam/publishExam', '发布考试', '考试管理', 'PUT'),
|
||||
('/exam/findExam', '根据ID获取考试', '考试管理', 'GET'),
|
||||
('/exam/getExamList', '获取考试列表', '考试管理', 'GET'),
|
||||
('/exam/getActiveExams', '获取进行中的考试列表', '考试管理', 'GET'),
|
||||
('/exam/getExamsByCourse', '根据课程获取考试列表', '考试管理', 'GET'),
|
||||
('/exam/getExamStatistics', '获取考试统计', '考试管理', 'GET'),
|
||||
|
||||
-- 学习记录管理API
|
||||
('/userLearning/createUserLearning', '创建学习记录', '学习记录管理', 'POST'),
|
||||
('/userLearning/deleteUserLearning', '删除学习记录', '学习记录管理', 'DELETE'),
|
||||
('/userLearning/deleteUserLearningByIds', '批量删除学习记录', '学习记录管理', 'DELETE'),
|
||||
('/userLearning/updateUserLearning', '更新学习记录', '学习记录管理', 'PUT'),
|
||||
('/userLearning/findUserLearning', '根据ID获取学习记录', '学习记录管理', 'GET'),
|
||||
('/userLearning/getUserLearningList', '获取学习记录列表', '学习记录管理', 'GET'),
|
||||
('/userLearning/getUserProgress', '获取用户学习进度', '学习记录管理', 'GET'),
|
||||
('/userLearning/getLearningStatistics', '获取用户学习统计', '学习记录管理', 'GET'),
|
||||
|
||||
-- 错题本管理API
|
||||
('/wrongQuestion/createWrongQuestion', '创建错题记录', '错题本管理', 'POST'),
|
||||
('/wrongQuestion/deleteWrongQuestion', '删除错题记录', '错题本管理', 'DELETE'),
|
||||
('/wrongQuestion/deleteWrongQuestionByIds', '批量删除错题记录', '错题本管理', 'DELETE'),
|
||||
('/wrongQuestion/updateWrongQuestion', '更新错题记录', '错题本管理', 'PUT'),
|
||||
('/wrongQuestion/markAsMastered', '标记题目为已掌握', '错题本管理', 'PUT'),
|
||||
('/wrongQuestion/findWrongQuestion', '根据ID获取错题记录', '错题本管理', 'GET'),
|
||||
('/wrongQuestion/getWrongQuestionList', '获取错题记录列表', '错题本管理', 'GET'),
|
||||
('/wrongQuestion/getUserWrongQuestions', '获取用户错题列表', '错题本管理', 'GET'),
|
||||
('/wrongQuestion/getWrongQuestionStatistics', '获取错题统计', '错题本管理', 'GET'),
|
||||
|
||||
-- 用户考试记录管理API
|
||||
('/userExam/createUserExam', '创建用户考试记录', '用户考试记录管理', 'POST'),
|
||||
('/userExam/deleteUserExam', '删除用户考试记录', '用户考试记录管理', 'DELETE'),
|
||||
('/userExam/deleteUserExamByIds', '批量删除用户考试记录', '用户考试记录管理', 'DELETE'),
|
||||
('/userExam/updateUserExam', '更新用户考试记录', '用户考试记录管理', 'PUT'),
|
||||
('/userExam/startExam', '开始考试', '用户考试记录管理', 'POST'),
|
||||
('/userExam/submitExam', '提交考试', '用户考试记录管理', 'PUT'),
|
||||
('/userExam/findUserExam', '根据ID获取用户考试记录', '用户考试记录管理', 'GET'),
|
||||
('/userExam/getUserExamList', '获取用户考试记录列表', '用户考试记录管理', 'GET'),
|
||||
('/userExam/getUserExamHistory', '获取用户考试历史', '用户考试记录管理', 'GET'),
|
||||
('/userExam/getUserExamStatistics', '获取用户考试统计', '用户考试记录管理', 'GET'),
|
||||
('/userExam/getExamRanking', '获取考试排行榜', '用户考试记录管理', 'GET');
|
||||
|
||||
-- =============================================
|
||||
-- 验证查询
|
||||
-- =============================================
|
||||
-- 验证字典是否创建成功
|
||||
-- SELECT d.name, d.type, COUNT(dd.id) as detail_count
|
||||
-- FROM sys_dictionaries d
|
||||
-- LEFT JOIN sys_dictionary_details dd ON d.id = dd.sys_dictionary_id
|
||||
-- WHERE d.type LIKE '%course%' OR d.type LIKE '%chapter%' OR d.type LIKE '%knowledge%'
|
||||
-- OR d.type LIKE '%question%' OR d.type LIKE '%learning%' OR d.type LIKE '%exam%'
|
||||
-- GROUP BY d.id, d.name, d.type
|
||||
-- ORDER BY d.id;
|
||||
|
||||
-- 验证菜单是否创建成功
|
||||
-- SELECT id, menu_level, parent_id, path, name, title, icon
|
||||
-- FROM sys_base_menus
|
||||
-- WHERE name IN ('learning', 'student') OR parent_id IN (
|
||||
-- SELECT id FROM sys_base_menus WHERE name IN ('learning', 'student')
|
||||
-- )
|
||||
-- ORDER BY menu_level, parent_id, sort;
|
||||
|
||||
-- 验证API是否创建成功
|
||||
-- SELECT api_group, COUNT(*) as api_count
|
||||
-- FROM sys_apis
|
||||
-- WHERE api_group IN ('课程管理', '章节管理', '知识点管理', '题目管理', '考试管理', '学习记录管理', '错题本管理')
|
||||
-- GROUP BY api_group
|
||||
-- ORDER BY api_group;
|
||||
|
||||
-- =============================================
|
||||
-- 执行说明
|
||||
-- =============================================
|
||||
-- 1. 此脚本包含学习系统的完整初始化配置
|
||||
-- 2. 包含8个数据字典和对应的字典详情
|
||||
-- 3. 包含教学管理和在线学习两个主菜单及其子菜单
|
||||
-- 4. 包含所有学习系统模块的API权限配置
|
||||
-- 5. 执行后可在系统管理中查看和管理这些配置
|
||||
-- 6. 如需配置用户角色权限,请执行 learning_system_roles.sql
|
||||
@@ -0,0 +1,225 @@
|
||||
-- =============================================
|
||||
-- 学习系统角色权限配置SQL脚本
|
||||
-- 创建教师角色和学生角色,并分配相应的菜单和API权限
|
||||
-- 注意:请先执行 learning_system_init.sql 再执行此文件
|
||||
-- =============================================
|
||||
|
||||
-- 1. 创建角色
|
||||
INSERT INTO `sys_authorities` (`authority_id`, `authority_name`, `parent_id`, `default_router`) VALUES
|
||||
(1001, '教师', 0, 'learning'),
|
||||
(1002, '学生', 0, 'student');
|
||||
|
||||
-- 2. 获取菜单ID
|
||||
SET @learning_menu_id = (SELECT id FROM sys_base_menus WHERE name = 'learning' AND menu_level = 0);
|
||||
SET @student_menu_id = (SELECT id FROM sys_base_menus WHERE name = 'student' AND menu_level = 0);
|
||||
|
||||
-- 获取教学管理子菜单ID
|
||||
SET @course_menu_id = (SELECT id FROM sys_base_menus WHERE name = 'course' AND parent_id = @learning_menu_id);
|
||||
SET @chapter_menu_id = (SELECT id FROM sys_base_menus WHERE name = 'chapter' AND parent_id = @learning_menu_id);
|
||||
SET @knowledge_menu_id = (SELECT id FROM sys_base_menus WHERE name = 'knowledgePoint' AND parent_id = @learning_menu_id);
|
||||
SET @question_menu_id = (SELECT id FROM sys_base_menus WHERE name = 'question' AND parent_id = @learning_menu_id);
|
||||
SET @exam_menu_id = (SELECT id FROM sys_base_menus WHERE name = 'exam' AND parent_id = @learning_menu_id);
|
||||
SET @records_menu_id = (SELECT id FROM sys_base_menus WHERE name = 'learningRecords' AND parent_id = @learning_menu_id);
|
||||
|
||||
-- 获取在线学习子菜单ID
|
||||
SET @dashboard_menu_id = (SELECT id FROM sys_base_menus WHERE name = 'dashboard' AND parent_id = @student_menu_id);
|
||||
SET @my_courses_menu_id = (SELECT id FROM sys_base_menus WHERE name = 'myCourses' AND parent_id = @student_menu_id);
|
||||
SET @online_exam_menu_id = (SELECT id FROM sys_base_menus WHERE name = 'onlineExam' AND parent_id = @student_menu_id);
|
||||
SET @wrong_questions_menu_id = (SELECT id FROM sys_base_menus WHERE name = 'wrongQuestions' AND parent_id = @student_menu_id);
|
||||
|
||||
-- 3. 为教师角色分配菜单权限
|
||||
INSERT INTO `sys_authority_menus` (`sys_base_menu_id`, `sys_authority_authority_id`) VALUES
|
||||
-- 教学管理主菜单
|
||||
(@learning_menu_id, 1001),
|
||||
-- 教学管理子菜单
|
||||
(@course_menu_id, 1001),
|
||||
(@chapter_menu_id, 1001),
|
||||
(@knowledge_menu_id, 1001),
|
||||
(@question_menu_id, 1001),
|
||||
(@exam_menu_id, 1001),
|
||||
(@records_menu_id, 1001);
|
||||
|
||||
-- 4. 为学生角色分配菜单权限
|
||||
INSERT INTO `sys_authority_menus` (`sys_base_menu_id`, `sys_authority_authority_id`) VALUES
|
||||
-- 在线学习主菜单
|
||||
(@student_menu_id, 1002),
|
||||
-- 在线学习子菜单
|
||||
(@dashboard_menu_id, 1002),
|
||||
(@my_courses_menu_id, 1002),
|
||||
(@online_exam_menu_id, 1002),
|
||||
(@wrong_questions_menu_id, 1002);
|
||||
|
||||
-- 5. 为教师角色分配API权限
|
||||
INSERT INTO `casbin_rule` (`ptype`, `v0`, `v1`, `v2`) VALUES
|
||||
-- 课程管理API权限
|
||||
('p', '1001', '/course/createCourse', 'POST'),
|
||||
('p', '1001', '/course/deleteCourse', 'DELETE'),
|
||||
('p', '1001', '/course/deleteCourseByIds', 'DELETE'),
|
||||
('p', '1001', '/course/updateCourse', 'PUT'),
|
||||
('p', '1001', '/course/findCourse', 'GET'),
|
||||
('p', '1001', '/course/getCourseList', 'GET'),
|
||||
('p', '1001', '/course/getPublishedCourses', 'GET'),
|
||||
|
||||
-- 章节管理API权限
|
||||
('p', '1001', '/chapter/createChapter', 'POST'),
|
||||
('p', '1001', '/chapter/deleteChapter', 'DELETE'),
|
||||
('p', '1001', '/chapter/deleteChapterByIds', 'DELETE'),
|
||||
('p', '1001', '/chapter/updateChapter', 'PUT'),
|
||||
('p', '1001', '/chapter/findChapter', 'GET'),
|
||||
('p', '1001', '/chapter/getChapterList', 'GET'),
|
||||
('p', '1001', '/chapter/getChaptersByCourse', 'GET'),
|
||||
|
||||
-- 知识点管理API权限
|
||||
('p', '1001', '/knowledgePoint/createKnowledgePoint', 'POST'),
|
||||
('p', '1001', '/knowledgePoint/deleteKnowledgePoint', 'DELETE'),
|
||||
('p', '1001', '/knowledgePoint/deleteKnowledgePointByIds', 'DELETE'),
|
||||
('p', '1001', '/knowledgePoint/updateKnowledgePoint', 'PUT'),
|
||||
('p', '1001', '/knowledgePoint/findKnowledgePoint', 'GET'),
|
||||
('p', '1001', '/knowledgePoint/getKnowledgePointList', 'GET'),
|
||||
('p', '1001', '/knowledgePoint/getKnowledgePointsByChapter', 'GET'),
|
||||
|
||||
-- 题目管理API权限
|
||||
('p', '1001', '/question/createQuestion', 'POST'),
|
||||
('p', '1001', '/question/deleteQuestion', 'DELETE'),
|
||||
('p', '1001', '/question/deleteQuestionByIds', 'DELETE'),
|
||||
('p', '1001', '/question/updateQuestion', 'PUT'),
|
||||
('p', '1001', '/question/findQuestion', 'GET'),
|
||||
('p', '1001', '/question/getQuestionList', 'GET'),
|
||||
('p', '1001', '/question/getQuestionsByKnowledgePoint', 'GET'),
|
||||
|
||||
-- 考试管理API权限
|
||||
('p', '1001', '/exam/createExam', 'POST'),
|
||||
('p', '1001', '/exam/deleteExam', 'DELETE'),
|
||||
('p', '1001', '/exam/deleteExamByIds', 'DELETE'),
|
||||
('p', '1001', '/exam/updateExam', 'PUT'),
|
||||
('p', '1001', '/exam/publishExam', 'PUT'),
|
||||
('p', '1001', '/exam/findExam', 'GET'),
|
||||
('p', '1001', '/exam/getExamList', 'GET'),
|
||||
('p', '1001', '/exam/getActiveExams', 'GET'),
|
||||
('p', '1001', '/exam/getExamsByCourse', 'GET'),
|
||||
('p', '1001', '/exam/getExamStatistics', 'GET'),
|
||||
|
||||
-- 学习记录管理API权限
|
||||
('p', '1001', '/userLearning/createUserLearning', 'POST'),
|
||||
('p', '1001', '/userLearning/deleteUserLearning', 'DELETE'),
|
||||
('p', '1001', '/userLearning/deleteUserLearningByIds', 'DELETE'),
|
||||
('p', '1001', '/userLearning/updateUserLearning', 'PUT'),
|
||||
('p', '1001', '/userLearning/findUserLearning', 'GET'),
|
||||
('p', '1001', '/userLearning/getUserLearningList', 'GET'),
|
||||
('p', '1001', '/userLearning/getUserProgress', 'GET'),
|
||||
('p', '1001', '/userLearning/getLearningStatistics', 'GET'),
|
||||
|
||||
-- 错题本管理API权限
|
||||
('p', '1001', '/wrongQuestion/createWrongQuestion', 'POST'),
|
||||
('p', '1001', '/wrongQuestion/deleteWrongQuestion', 'DELETE'),
|
||||
('p', '1001', '/wrongQuestion/deleteWrongQuestionByIds', 'DELETE'),
|
||||
('p', '1001', '/wrongQuestion/updateWrongQuestion', 'PUT'),
|
||||
('p', '1001', '/wrongQuestion/markAsMastered', 'PUT'),
|
||||
('p', '1001', '/wrongQuestion/findWrongQuestion', 'GET'),
|
||||
('p', '1001', '/wrongQuestion/getWrongQuestionList', 'GET'),
|
||||
('p', '1001', '/wrongQuestion/getUserWrongQuestions', 'GET'),
|
||||
('p', '1001', '/wrongQuestion/getWrongQuestionStatistics', 'GET'),
|
||||
|
||||
-- 用户考试记录管理API权限
|
||||
('p', '1001', '/userExam/createUserExam', 'POST'),
|
||||
('p', '1001', '/userExam/deleteUserExam', 'DELETE'),
|
||||
('p', '1001', '/userExam/deleteUserExamByIds', 'DELETE'),
|
||||
('p', '1001', '/userExam/updateUserExam', 'PUT'),
|
||||
('p', '1001', '/userExam/startExam', 'POST'),
|
||||
('p', '1001', '/userExam/submitExam', 'PUT'),
|
||||
('p', '1001', '/userExam/findUserExam', 'GET'),
|
||||
('p', '1001', '/userExam/getUserExamList', 'GET'),
|
||||
('p', '1001', '/userExam/getUserExamHistory', 'GET'),
|
||||
('p', '1001', '/userExam/getUserExamStatistics', 'GET'),
|
||||
('p', '1001', '/userExam/getExamRanking', 'GET');
|
||||
|
||||
-- 6. 为学生角色分配API权限(只读权限和学生相关功能)
|
||||
INSERT INTO `casbin_rule` (`ptype`, `v0`, `v1`, `v2`) VALUES
|
||||
-- 课程查看权限
|
||||
('p', '1002', '/course/findCourse', 'GET'),
|
||||
('p', '1002', '/course/getPublishedCourses', 'GET'),
|
||||
|
||||
-- 章节查看权限
|
||||
('p', '1002', '/chapter/findChapter', 'GET'),
|
||||
('p', '1002', '/chapter/getChaptersByCourse', 'GET'),
|
||||
|
||||
-- 知识点查看权限
|
||||
('p', '1002', '/knowledgePoint/findKnowledgePoint', 'GET'),
|
||||
('p', '1002', '/knowledgePoint/getKnowledgePointsByChapter', 'GET'),
|
||||
|
||||
-- 题目查看权限
|
||||
('p', '1002', '/question/findQuestion', 'GET'),
|
||||
('p', '1002', '/question/getQuestionsByKnowledgePoint', 'GET'),
|
||||
|
||||
-- 考试相关权限
|
||||
('p', '1002', '/exam/findExam', 'GET'),
|
||||
('p', '1002', '/exam/getActiveExams', 'GET'),
|
||||
('p', '1002', '/exam/getExamsByCourse', 'GET'),
|
||||
|
||||
-- 学习记录权限(学生可以创建和查看自己的学习记录)
|
||||
('p', '1002', '/userLearning/createUserLearning', 'POST'),
|
||||
('p', '1002', '/userLearning/updateUserLearning', 'PUT'),
|
||||
('p', '1002', '/userLearning/findUserLearning', 'GET'),
|
||||
('p', '1002', '/userLearning/getUserProgress', 'GET'),
|
||||
('p', '1002', '/userLearning/getLearningStatistics', 'GET'),
|
||||
|
||||
-- 错题本权限(学生可以管理自己的错题)
|
||||
('p', '1002', '/wrongQuestion/createWrongQuestion', 'POST'),
|
||||
('p', '1002', '/wrongQuestion/updateWrongQuestion', 'PUT'),
|
||||
('p', '1002', '/wrongQuestion/markAsMastered', 'PUT'),
|
||||
('p', '1002', '/wrongQuestion/findWrongQuestion', 'GET'),
|
||||
('p', '1002', '/wrongQuestion/getUserWrongQuestions', 'GET'),
|
||||
('p', '1002', '/wrongQuestion/getWrongQuestionStatistics', 'GET'),
|
||||
|
||||
-- 用户考试记录权限(学生可以参加考试和查看自己的考试记录)
|
||||
('p', '1002', '/userExam/startExam', 'POST'),
|
||||
('p', '1002', '/userExam/submitExam', 'PUT'),
|
||||
('p', '1002', '/userExam/findUserExam', 'GET'),
|
||||
('p', '1002', '/userExam/getUserExamHistory', 'GET'),
|
||||
('p', '1002', '/userExam/getUserExamStatistics', 'GET'),
|
||||
('p', '1002', '/userExam/getExamRanking', 'GET');
|
||||
|
||||
-- =============================================
|
||||
-- 创建测试用户(可选)
|
||||
-- =============================================
|
||||
|
||||
-- 创建教师测试用户
|
||||
-- INSERT INTO `sys_users` (`uuid`, `username`, `password`, `nick_name`, `side_mode`, `header_img`, `base_color`, `authority_id`, `phone`, `email`) VALUES
|
||||
-- ('teacher-uuid-001', 'teacher', '$2a$10$we19gyu.geUOEhrMkrVzz.VT64GdGC6IThOvAbf8hlkOOZRxdCdli', '张老师', 'dark', 'https://qmplusimg.henrongyi.top/gva_header.jpg', '#fff', 1001, '13800000001', 'teacher@example.com');
|
||||
|
||||
-- 创建学生测试用户
|
||||
-- INSERT INTO `sys_users` (`uuid`, `username`, `password`, `nick_name`, `side_mode`, `header_img`, `base_color`, `authority_id`, `phone`, `email`) VALUES
|
||||
-- ('student-uuid-001', 'student', '$2a$10$we19gyu.geUOEhrMkrVzz.VT64GdGC6IThOvAbf8hlkOOZRxdCdli', '李同学', 'dark', 'https://qmplusimg.henrongyi.top/gva_header.jpg', '#fff', 1002, '13800000002', 'student@example.com');
|
||||
|
||||
-- =============================================
|
||||
-- 验证查询
|
||||
-- =============================================
|
||||
|
||||
-- 验证角色是否创建成功
|
||||
-- SELECT authority_id, authority_name, parent_id, default_router FROM sys_authorities WHERE authority_id IN (1001, 1002);
|
||||
|
||||
-- 验证菜单权限是否分配成功
|
||||
-- SELECT sa.authority_name, sbm.title, sbm.path
|
||||
-- FROM sys_authority_menus sam
|
||||
-- JOIN sys_authorities sa ON sam.sys_authority_authority_id = sa.authority_id
|
||||
-- JOIN sys_base_menus sbm ON sam.sys_base_menu_id = sbm.id
|
||||
-- WHERE sa.authority_id IN (1001, 1002)
|
||||
-- ORDER BY sa.authority_id, sbm.sort;
|
||||
|
||||
-- 验证API权限是否分配成功
|
||||
-- SELECT v0 as role, COUNT(*) as api_count
|
||||
-- FROM casbin_rule
|
||||
-- WHERE v0 IN ('1001', '1002') AND ptype = 'p'
|
||||
-- GROUP BY v0;
|
||||
|
||||
-- =============================================
|
||||
-- 使用说明
|
||||
-- =============================================
|
||||
-- 1. 执行此脚本前请确保已执行 learning_system_init.sql
|
||||
-- 2. 此脚本创建了两个角色:1001(教师)和 1002(学生)
|
||||
-- 3. 教师角色拥有教学管理的完整权限
|
||||
-- 4. 学生角色拥有在线学习的相关权限
|
||||
-- 5. 可以取消注释测试用户部分来创建测试账号
|
||||
-- 6. 默认密码为:123456
|
||||
-- 7. 教师登录后会看到"教学管理"菜单
|
||||
-- 8. 学生登录后会看到"在线学习"菜单并跳转到学习首页
|
||||
@@ -0,0 +1,142 @@
|
||||
# 学习系统SQL执行指南
|
||||
|
||||
## 📋 执行顺序
|
||||
|
||||
请按照以下顺序执行SQL文件:
|
||||
|
||||
### 1. 基础初始化(必须执行)
|
||||
```sql
|
||||
-- 执行学习系统基础配置
|
||||
source server/sql/01_learning_system_init.sql;
|
||||
```
|
||||
|
||||
**包含内容:**
|
||||
- ✅ 8个数据字典(课程难度、课程状态、章节状态、知识点状态、题目类型、题目难度、学习状态、考试状态)
|
||||
- ✅ 所有字典详情配置
|
||||
- ✅ 教学管理和在线学习两个主菜单
|
||||
- ✅ 10个子菜单(6个教学管理 + 4个学生学习)
|
||||
- ✅ 70个API权限配置(8个模块完整覆盖)
|
||||
|
||||
### 2. 角色权限配置(推荐执行)
|
||||
```sql
|
||||
-- 执行角色权限配置
|
||||
source server/sql/02_learning_system_roles.sql;
|
||||
```
|
||||
|
||||
**包含内容:**
|
||||
- ✅ 创建教师角色(teacher)和学生角色(student)
|
||||
- ✅ 为教师分配教学管理菜单和完整API权限(70个API)
|
||||
- ✅ 为学生分配在线学习菜单和相应API权限(30个API)
|
||||
- ✅ 可选的测试用户创建
|
||||
|
||||
## 🎯 执行后效果
|
||||
|
||||
### 教师用户登录后:
|
||||
- 默认跳转到 `/learning` 路径
|
||||
- 看到"教学管理"菜单,包含:
|
||||
- 课程管理
|
||||
- 章节管理
|
||||
- 知识点管理
|
||||
- 题目管理
|
||||
- 考试管理
|
||||
- 学习记录
|
||||
- 拥有所有教学相关API的完整权限
|
||||
|
||||
### 学生用户登录后:
|
||||
- 默认跳转到 `/student` 路径
|
||||
- 看到"在线学习"菜单,包含:
|
||||
- 学习首页(现代化Dashboard)
|
||||
- 我的课程
|
||||
- 在线考试
|
||||
- 我的错题
|
||||
- 拥有学习相关API的查看和个人数据管理权限
|
||||
|
||||
## 🔧 可选配置
|
||||
|
||||
### 创建测试用户
|
||||
如果需要测试用户,可以取消注释 `learning_system_roles.sql` 中的测试用户部分:
|
||||
|
||||
```sql
|
||||
-- 教师测试账号
|
||||
用户名: teacher
|
||||
密码: 123456
|
||||
|
||||
-- 学生测试账号
|
||||
用户名: student
|
||||
密码: 123456
|
||||
```
|
||||
|
||||
## 📊 验证执行结果
|
||||
|
||||
### 1. 验证字典配置
|
||||
```sql
|
||||
SELECT d.name, d.type, COUNT(dd.id) as detail_count
|
||||
FROM sys_dictionaries d
|
||||
LEFT JOIN sys_dictionary_details dd ON d.id = dd.sys_dictionary_id
|
||||
WHERE d.type LIKE '%course%' OR d.type LIKE '%chapter%' OR d.type LIKE '%knowledge%'
|
||||
OR d.type LIKE '%question%' OR d.type LIKE '%learning%' OR d.type LIKE '%exam%'
|
||||
GROUP BY d.id, d.name, d.type
|
||||
ORDER BY d.id;
|
||||
```
|
||||
|
||||
### 2. 验证菜单配置
|
||||
```sql
|
||||
SELECT id, menu_level, parent_id, path, name, title, icon
|
||||
FROM sys_base_menus
|
||||
WHERE name IN ('learning', 'student') OR parent_id IN (
|
||||
SELECT id FROM sys_base_menus WHERE name IN ('learning', 'student')
|
||||
)
|
||||
ORDER BY menu_level, parent_id, sort;
|
||||
```
|
||||
|
||||
### 3. 验证API权限
|
||||
```sql
|
||||
SELECT api_group, COUNT(*) as api_count
|
||||
FROM sys_apis
|
||||
WHERE api_group IN ('课程管理', '章节管理', '知识点管理', '题目管理', '考试管理', '学习记录管理', '错题本管理', '用户考试记录管理')
|
||||
GROUP BY api_group
|
||||
ORDER BY api_group;
|
||||
```
|
||||
|
||||
### 4. 验证角色权限
|
||||
```sql
|
||||
SELECT v0 as role, COUNT(*) as api_count
|
||||
FROM casbin_rule
|
||||
WHERE v0 IN ('teacher', 'student') AND ptype = 'p'
|
||||
GROUP BY v0;
|
||||
```
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
1. **执行顺序很重要**:必须先执行 `learning_system_init.sql`,再执行 `learning_system_roles.sql`
|
||||
2. **数据库备份**:执行前建议备份数据库
|
||||
3. **权限检查**:确保数据库用户有足够的权限执行这些SQL语句
|
||||
4. **重复执行**:如果需要重复执行,请先清理相关数据或使用 `INSERT IGNORE` 语句
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
如果您想快速体验完整的学习系统,只需执行:
|
||||
|
||||
```bash
|
||||
# 进入MySQL命令行
|
||||
mysql -u your_username -p your_database
|
||||
|
||||
# 执行初始化脚本
|
||||
source server/sql/01_learning_system_init.sql;
|
||||
|
||||
# 执行角色权限脚本
|
||||
source server/sql/02_learning_system_roles.sql;
|
||||
|
||||
# 退出MySQL
|
||||
exit;
|
||||
```
|
||||
|
||||
执行完成后,重启您的应用程序,就可以体验完整的学习系统了!
|
||||
|
||||
## 📞 问题反馈
|
||||
|
||||
如果在执行过程中遇到问题,请检查:
|
||||
1. 数据库连接是否正常
|
||||
2. 用户权限是否足够
|
||||
3. 表结构是否已通过应用程序自动创建
|
||||
4. SQL语法是否与您的MySQL版本兼容
|
||||
@@ -0,0 +1,60 @@
|
||||
学习系统SQL执行说明
|
||||
====================
|
||||
|
||||
📋 执行顺序(重要!)
|
||||
====================
|
||||
|
||||
第一步:基础配置
|
||||
mysql> source server/sql/01_learning_system_init.sql;
|
||||
|
||||
第二步:角色权限
|
||||
mysql> source server/sql/02_learning_system_roles.sql;
|
||||
|
||||
🎯 执行后效果
|
||||
====================
|
||||
|
||||
✅ 数据字典:8个完整字典配置
|
||||
✅ 菜单权限:2个主菜单 + 10个子菜单
|
||||
✅ API权限:70个接口权限配置
|
||||
✅ 用户角色:教师角色(1001) + 学生角色(1002)
|
||||
|
||||
📊 系统模块统计
|
||||
====================
|
||||
|
||||
后端模块:8个核心模块
|
||||
- Course(课程管理)
|
||||
- Chapter(章节管理)
|
||||
- KnowledgePoint(知识点管理)
|
||||
- Question(题目管理)
|
||||
- Exam(考试管理)
|
||||
- UserLearning(学习记录管理)
|
||||
- WrongQuestion(错题本管理)
|
||||
- UserExam(用户考试记录管理)
|
||||
|
||||
前端页面:完整的教师端和学生端界面
|
||||
- 教师端:教学管理界面(6个管理页面)
|
||||
- 学生端:在线学习界面(4个学习页面)
|
||||
|
||||
🔧 测试账号(可选)
|
||||
====================
|
||||
|
||||
如需测试账号,请取消注释02文件中的测试用户部分:
|
||||
|
||||
教师账号:teacher / 123456
|
||||
学生账号:student / 123456
|
||||
|
||||
⚠️ 注意事项
|
||||
====================
|
||||
|
||||
1. 必须按顺序执行:01 → 02
|
||||
2. 执行前请备份数据库
|
||||
3. 确保数据库用户有足够权限
|
||||
4. 执行完成后重启应用程序
|
||||
|
||||
🚀 完成后体验
|
||||
====================
|
||||
|
||||
教师登录:看到"教学管理"菜单,跳转到/learning
|
||||
学生登录:看到"在线学习"菜单,跳转到/student(现代化Dashboard)
|
||||
|
||||
详细说明请查看:README.md
|
||||
Reference in New Issue
Block a user