1
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user