From 7f7c2a6df9cd7267e4d292c563599df4ef52db63 Mon Sep 17 00:00:00 2001 From: wangguixi Date: Thu, 13 Nov 2025 19:11:56 +0800 Subject: [PATCH] =?UTF-8?q?11.13=20=E6=89=80=E6=9C=89=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E7=9A=84=E5=8A=9F=E8=83=BD=E9=83=BD=E5=B7=B2=E5=85=A8=E9=83=A8?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Knowledge_Test_Go/api/v1/questionBank.go | 26 +++- Knowledge_Test_Go/internal/cmd/cmd.go | 2 + Knowledge_Test_Go/internal/controller/knowledge.go | 42 ++---- .../internal/controller/questionBank.go | 13 ++ .../internal/logic/knowledge/knowledge.go | 39 ++++++ .../internal/logic/questionBank/questionBank.go | 152 +++++++++++++++++++-- Knowledge_Test_Go/internal/service/knowledge.go | 2 + .../internal/service/question_bank.go | 4 +- 8 files changed, 234 insertions(+), 46 deletions(-) diff --git a/Knowledge_Test_Go/api/v1/questionBank.go b/Knowledge_Test_Go/api/v1/questionBank.go index c5c0b6a..1b732a1 100644 --- a/Knowledge_Test_Go/api/v1/questionBank.go +++ b/Knowledge_Test_Go/api/v1/questionBank.go @@ -51,6 +51,16 @@ type GetUserScoresRes struct { Score int `json:"score"` } +// GetCourseReq 获取用户课程请求 +type GetCourseReq struct { + Jwcode int `v:"required" dc:"精网号"` +} + +// GetCourseRes 获取用户课程响应 +type GetCourseRes struct { + CrName []string `json:"cr_name"` +} + // GetWrongQuestionsRes 错题详情 type GetWrongQuestionsRes struct { GetQuestionsRes `json:"question"` @@ -103,8 +113,8 @@ type QuestionDelReq struct { Id int `json:"id"` } -// UserScoreOutput 用户成绩输出 -type UserScoreOutput struct { +// UserScoreOutputRes 获取用户成绩列表响应 +type UserScoreOutputRes struct { Id int `json:"id"` UserName string `json:"user_name"` UserIdentity string `json:"user_identity"` @@ -115,11 +125,13 @@ type UserScoreOutput struct { // UserScoreOutputReq 获取用户成绩列表请求 type UserScoreOutputReq struct { - //UserName string `json:"user_name"` - //UserIdentity string `json:"user_identity"` - //Jwcode int `json:"jwcode"` - Page int `json:"page"` - PageSize int `json:"page_size"` + UserName string `json:"user_name"` // 用户名(精准查询) + UserIdentity string `json:"user_identity"` // 用户身份(过滤) + Jwcode int `json:"jwcode"` // 精网号(精准查询) + StartTime string `json:"start_time"` // 开始时间(提交时间范围) + EndTime string `json:"end_time"` // 结束时间(提交时间范围) + Page int `json:"page"` + PageSize int `json:"page_size"` } // ErrorOutPutUserReq 获取错题用户列表请求 diff --git a/Knowledge_Test_Go/internal/cmd/cmd.go b/Knowledge_Test_Go/internal/cmd/cmd.go index 3b966b9..568e8ba 100644 --- a/Knowledge_Test_Go/internal/cmd/cmd.go +++ b/Knowledge_Test_Go/internal/cmd/cmd.go @@ -27,6 +27,7 @@ var ( group.POST("/knowledge/submit", controller.NewKnowledgeTest().SubmitAnswers) // 提交答案 group.POST("/knowledge/scores", controller.NewKnowledgeTest().GetUserScores) // 获取用户成绩 group.POST("/knowledge/wrong-questions", controller.NewKnowledgeTest().GetWrongQuestions) // 获取错题列表 + group.POST("/knowledge/course", controller.NewKnowledgeTest().GetCourse) // 获取课程 }) // 后台路由组 @@ -40,6 +41,7 @@ var ( group.POST("/user/score", controller.NewQuestionBank().UserScoreOutput) // 获取用户成绩 group.POST("/questions/user", controller.NewQuestionBank().ErrorOutPutUser) // 获取错题用户列表 group.POST("/questions/export", controller.NewQuestionBank().ExportQuestion) // 导出题目数据到 Excel + group.POST("/user/export", controller.NewQuestionBank().ExportUserScore) //导出用户成绩到 Excel }) s.Run() diff --git a/Knowledge_Test_Go/internal/controller/knowledge.go b/Knowledge_Test_Go/internal/controller/knowledge.go index 4c944f2..ab6942b 100644 --- a/Knowledge_Test_Go/internal/controller/knowledge.go +++ b/Knowledge_Test_Go/internal/controller/knowledge.go @@ -46,6 +46,20 @@ func (c *cKnowledgeTest) GetUserScores(r *ghttp.Request) { response.JsonExit(r, 200, "success", res) } +// GetScores 获取课程推荐 +func (c *cKnowledgeTest) GetCourse(r *ghttp.Request) { + ctx := r.GetCtx() + var req *v1.GetCourseReq + if err := r.Parse(&req); err != nil { + response.JsonExit(r, 400, err.Error()) + } + res, err := service.KnowledgeTest().GetCourse(ctx, req) + if err != nil { + response.JsonExit(r, 400, err.Error()) + } + response.JsonExit(r, 200, "success", res) +} + // SubmitAnswers 提交答案 func (c *cKnowledgeTest) SubmitAnswers(r *ghttp.Request) { ctx := r.GetCtx() @@ -78,31 +92,3 @@ func (c *cKnowledgeTest) GetWrongQuestions(r *ghttp.Request) { "total": total, }) } - -// -//// GetUserScores 获取用户成绩 -//func (c *KnowledgeTest) GetUserScores(r *ghttp.Request) { -// var req v1.GetUserScoresReq -// if err := r.Parse(&req); err != nil { -// r.Response.WriteJson(g.Map{ -// "code": 500, -// "msg": err.Error(), -// }) -// return -// } -// -// res, err := knowledge.NewKnowledgeTestLogic(r.Context()).GetUserScores(&req) -// if err != nil { -// r.Response.WriteJson(g.Map{ -// "code": 500, -// "msg": err.Error(), -// }) -// return -// } -// -// r.Response.WriteJson(g.Map{ -// "code": 200, -// "msg": "success", -// "data": res, -// }) -//}*/ diff --git a/Knowledge_Test_Go/internal/controller/questionBank.go b/Knowledge_Test_Go/internal/controller/questionBank.go index e50e657..9814342 100644 --- a/Knowledge_Test_Go/internal/controller/questionBank.go +++ b/Knowledge_Test_Go/internal/controller/questionBank.go @@ -107,3 +107,16 @@ func (c *cQuestionBank) ExportQuestion(r *ghttp.Request) { } service.QuestionBank().ExportQuestion(r, ctx, req) } + +// ExportUserScore 导出用户成绩到 Excel +func (c *cQuestionBank) ExportUserScore(r *ghttp.Request) { + ctx := r.GetCtx() + var req *v1.UserScoreOutputReq + if err := r.Parse(&req); err != nil { + response.JsonExit(r, 400, err.Error()) + return + } + + // 调用服务层的导出方法 + service.QuestionBank().ExportUserScore(r, ctx, req) +} diff --git a/Knowledge_Test_Go/internal/logic/knowledge/knowledge.go b/Knowledge_Test_Go/internal/logic/knowledge/knowledge.go index c8c0eeb..707bd4a 100644 --- a/Knowledge_Test_Go/internal/logic/knowledge/knowledge.go +++ b/Knowledge_Test_Go/internal/logic/knowledge/knowledge.go @@ -39,6 +39,45 @@ func (s *sKnowledgeTest) GetUserScores(ctx context.Context, req *v1.GetUserScore // ----------------------------------------------------------------------------------------------------------------------------------------------- +// GetCourse 获取用户课程 +func (s *sKnowledgeTest) GetCourse(ctx context.Context, req *v1.GetCourseReq) (res []*v1.GetCourseRes, err error) { + // 使用临时结构体接收数据 + type Course struct { + CrName string `json:"cr_name"` + } + var courses []Course + + // 查询数据 + err = g.Model("question_record a"). + LeftJoin("question_bank b", "a.question_bank_id = b.id"). + LeftJoin("course_recommend c", "b.course_recommendation_id = c.id"). + Where("a.jwcode = ?", req.Jwcode). + Where("c.cr_name IS NOT NULL"). + Where("c.cr_name != ''"). + Fields("DISTINCT c.cr_name"). + Ctx(ctx). + Scan(&courses) + + if err != nil { + return nil, fmt.Errorf("查询课程名称失败: %w", err) + } + + // 提取纯字符串 + courseNames := make([]string, 0, len(courses)) + for _, course := range courses { + courseNames = append(courseNames, course.CrName) + } + + // 创建响应 + res = append(res, &v1.GetCourseRes{ + CrName: courseNames, + }) + + return res, nil +} + +// ----------------------------------------------------------------------------------------------------------------------------------------------- + // SubmitAnswers 提交答案并计算分数 func (s *sKnowledgeTest) SubmitAnswers(ctx context.Context, req *v1.SubmitAnswersReq) (*v1.SubmitAnswersRes, error) { if len(req.Answers) == 0 { diff --git a/Knowledge_Test_Go/internal/logic/questionBank/questionBank.go b/Knowledge_Test_Go/internal/logic/questionBank/questionBank.go index 8e719f8..e80c951 100644 --- a/Knowledge_Test_Go/internal/logic/questionBank/questionBank.go +++ b/Knowledge_Test_Go/internal/logic/questionBank/questionBank.go @@ -123,10 +123,36 @@ func (s *sQuestionBank) QuestionDel(ctx context.Context, req *v1.QuestionDelReq) //------------------------------------------------------------------------------------------------------------------------------- // UserScoreOutput 获取用户成绩列表 -func (s *sQuestionBank) UserScoreOutput(ctx context.Context, req *v1.UserScoreOutputReq) (res []*v1.UserScoreOutput, total int, err error) { +func (s *sQuestionBank) UserScoreOutput(ctx context.Context, req *v1.UserScoreOutputReq) (res []*v1.UserScoreOutputRes, total int, err error) { db := g.Model("user a"). - RightJoin("total_score b", "a.jwcode=b.jwcode"). + RightJoin("total_score b", "a.jwcode = b.jwcode"). Fields("a.id", "a.user_name", "a.user_identity", "a.jwcode", "b.created_at", "b.score") + + // 添加查询条件 + if req.UserName != "" { + db = db.Where("a.user_name = ?", req.UserName) + } + + if req.UserIdentity != "" { + db = db.Where("a.user_identity = ?", req.UserIdentity) + } + + if req.Jwcode != 0 { + db = db.Where("a.jwcode = ?", req.Jwcode) + } + + // 时间范围查询 + if req.StartTime != "" && req.EndTime != "" { + db = db.Where("b.created_at BETWEEN ? AND ?", req.StartTime, req.EndTime) + } else if req.StartTime != "" { + db = db.Where("b.created_at >= ?", req.StartTime) + } else if req.EndTime != "" { + db = db.Where("b.created_at <= ?", req.EndTime) + } + + // 添加排序,默认按创建时间倒序 + db = db.OrderDesc("b.created_at") + err = db.Page(req.Page, req.PageSize).ScanAndCount(&res, &total, false) return } @@ -200,7 +226,7 @@ func (s *sQuestionBank) ExportQuestion(r *ghttp.Request, ctx context.Context, re fileName := "题目数据导出.xlsx" // === 4. 设置表头 === - headers := []string{"ID", "题干", "选项A", "选项B", "选项C", "选项D", "正确答案", "题目类型", "推荐课程", "引用次数", "错误次数", "错误率"} + headers := []string{"序号", "ID", "题干", "选项A", "选项B", "选项C", "选项D", "正确答案", "题目类型", "推荐课程", "引用次数", "错误次数", "错误率"} for i, header := range headers { col := string('A' + i) excelFile.SetCellValue(sheetName, col+"1", header) @@ -245,13 +271,119 @@ func (s *sQuestionBank) ExportQuestion(r *ghttp.Request, ctx context.Context, re excelFile.SetCellStyle(sheetName, "A1", "L"+strconv.Itoa(lastRow), style) // 设置列宽 - excelFile.SetColWidth(sheetName, "A", "A", 10) // ID - excelFile.SetColWidth(sheetName, "B", "B", 40) // 题干 - excelFile.SetColWidth(sheetName, "C", "F", 20) // 选项A-D - excelFile.SetColWidth(sheetName, "G", "G", 12) // 正确答案 - excelFile.SetColWidth(sheetName, "H", "H", 15) // 题目类型 - excelFile.SetColWidth(sheetName, "I", "I", 20) // 推荐课程 - excelFile.SetColWidth(sheetName, "J", "L", 12) // 统计信息 + excelFile.SetColWidth(sheetName, "B", "B", 10) // ID + excelFile.SetColWidth(sheetName, "C", "C", 40) // 题干 + excelFile.SetColWidth(sheetName, "D", "G", 20) // 选项A-D + excelFile.SetColWidth(sheetName, "H", "H", 12) // 正确答案 + excelFile.SetColWidth(sheetName, "I", "I", 15) // 题目类型 + excelFile.SetColWidth(sheetName, "J", "J", 20) // 推荐课程 + excelFile.SetColWidth(sheetName, "K", "M", 12) // 统计信息 + + // === 7. 输出 Excel 文件 === + buffer, err := excelFile.WriteToBuffer() + if err != nil { + response.JsonExit(r, 400, "生成Excel文件失败", err.Error()) + return + } + + // 设置响应头 + r.Response.Header().Set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") + r.Response.Header().Set("Content-Disposition", "attachment; filename="+fileName) + r.Response.Write(buffer.Bytes()) +} + +//------------------------------------------------------------------------------------------------------------------------------- + +// ExportUserScore 导出用户成绩数据到 Excel +func (s *sQuestionBank) ExportUserScore(r *ghttp.Request, ctx context.Context, req *v1.UserScoreOutputReq) { + // === 1. 数据查询和验证 === + var result []*v1.UserScoreOutputRes + + // 查询总数 + _, total, err := service.QuestionBank().UserScoreOutput(ctx, req) + if err != nil { + response.JsonExit(r, 400, "查询失败", err.Error()) + return + } + + // 数据量验证 + if total == 0 { + response.JsonExit(r, 400, "查询结果为空") + return + } + if total > 20000 { + response.JsonExit(r, 400, "导出数据过多,请添加筛选条件后再导出") + return + } + + // === 2. 分批查询数据 === + var num int + req.PageSize = 1000 + for { + res, total, err := service.QuestionBank().UserScoreOutput(ctx, req) + if err != nil { + response.JsonExit(r, 400, "分批查询失败", err.Error()) + return + } + result = append(result, res...) + num += req.PageSize + if num >= total { + break + } + req.Page++ + } + + // === 3. 创建 Excel 文件 === + excelFile := excelize.NewFile() + sheetName := "Sheet1" + fileName := "用户成绩导出.xlsx" + + // === 4. 设置表头 === + headers := []string{"序号", "ID", "用户名", "用户身份", "精网号", "成绩", "创建时间"} + for i, header := range headers { + col := string('A' + i) + excelFile.SetCellValue(sheetName, col+"1", header) + } + + // === 5. 写入数据 === + rowIndex := 2 + for _, userScore := range result { + excelFile.SetCellValue(sheetName, "A"+strconv.Itoa(rowIndex), rowIndex-1) + excelFile.SetCellValue(sheetName, "B"+strconv.Itoa(rowIndex), userScore.Id) + excelFile.SetCellValue(sheetName, "C"+strconv.Itoa(rowIndex), userScore.UserName) + excelFile.SetCellValue(sheetName, "D"+strconv.Itoa(rowIndex), userScore.UserIdentity) + excelFile.SetCellValue(sheetName, "E"+strconv.Itoa(rowIndex), userScore.Jwcode) + excelFile.SetCellValue(sheetName, "F"+strconv.Itoa(rowIndex), userScore.Score) + excelFile.SetCellValue(sheetName, "G"+strconv.Itoa(rowIndex), userScore.CreatedAt) + + rowIndex++ + } + + // === 6. 样式设置 === + lastRow := rowIndex - 1 + + // 创建样式 + style, err := excelFile.NewStyle(&excelize.Style{ + Alignment: &excelize.Alignment{ + Horizontal: "center", + Vertical: "center", + }, + }) + if err != nil { + response.JsonExit(r, 400, "创建样式失败", err.Error()) + return + } + + // 应用样式 + excelFile.SetCellStyle(sheetName, "A1", "F"+strconv.Itoa(lastRow), style) + + // 设置列宽 + excelFile.SetColWidth(sheetName, "B", "B", 10) // ID + excelFile.SetColWidth(sheetName, "C", "C", 20) // 用户名 + excelFile.SetColWidth(sheetName, "D", "D", 15) // 用户身份 + excelFile.SetColWidth(sheetName, "E", "E", 15) // 精网号 + excelFile.SetColWidth(sheetName, "F", "F", 10) // 成绩 + excelFile.SetColWidth(sheetName, "G", "G", 20) // 创建时间 // === 7. 输出 Excel 文件 === buffer, err := excelFile.WriteToBuffer() diff --git a/Knowledge_Test_Go/internal/service/knowledge.go b/Knowledge_Test_Go/internal/service/knowledge.go index 2ad0bab..98e5b4d 100644 --- a/Knowledge_Test_Go/internal/service/knowledge.go +++ b/Knowledge_Test_Go/internal/service/knowledge.go @@ -16,6 +16,8 @@ type ( GetQuestions(ctx context.Context, req *v1.GetQuestionsReq) (res []*v1.GetQuestionsRes, total int, err error) // GetUserScores 获取用户成绩 GetUserScores(ctx context.Context, req *v1.GetUserScoresReq) (res []*v1.GetUserScoresRes, err error) + // GetCourse 获取用户课程 + GetCourse(ctx context.Context, req *v1.GetCourseReq) (res []*v1.GetCourseRes, err error) // SubmitAnswers 提交答案并计算分数 SubmitAnswers(ctx context.Context, req *v1.SubmitAnswersReq) (*v1.SubmitAnswersRes, error) // GetWrongQuestions 获取错题列表 diff --git a/Knowledge_Test_Go/internal/service/question_bank.go b/Knowledge_Test_Go/internal/service/question_bank.go index c366b9c..eb0e145 100644 --- a/Knowledge_Test_Go/internal/service/question_bank.go +++ b/Knowledge_Test_Go/internal/service/question_bank.go @@ -21,11 +21,13 @@ type ( // QuestionDel 删除题目 QuestionDel(ctx context.Context, req *v1.QuestionDelReq) (res string, err error) // UserScoreOutput 获取用户成绩列表 - UserScoreOutput(ctx context.Context, req *v1.UserScoreOutputReq) (res []*v1.UserScoreOutput, total int, err error) + UserScoreOutput(ctx context.Context, req *v1.UserScoreOutputReq) (res []*v1.UserScoreOutputRes, total int, err error) // ErrorOutPutUser 错题统计出错用户 ErrorOutPutUser(ctx context.Context, req *v1.ErrorOutPutUserReq) (res []*v1.ErrorOutPutUserRes, total int, err error) // ExportQuestion 导出题目数据到 Excel ExportQuestion(r *ghttp.Request, ctx context.Context, req *v1.QuestionOutputReq) + // ExportUserScore 导出用户成绩数据到 Excel + ExportUserScore(r *ghttp.Request, ctx context.Context, req *v1.UserScoreOutputReq) } )