diff --git a/Knowledge_Test_Go/api/v1/questionBank.go b/Knowledge_Test_Go/api/v1/questionBank.go index 5510091..c5c0b6a 100644 --- a/Knowledge_Test_Go/api/v1/questionBank.go +++ b/Knowledge_Test_Go/api/v1/questionBank.go @@ -58,14 +58,6 @@ type GetWrongQuestionsRes struct { CorrectAnswer string `json:"correctAnswer" dc:"正确答案"` } -// TotalScoreOutput 成绩输出 -type TotalScoreOutput struct { - Id int `json:"id"` - Jwcode int `json:"jwcode"` - Score int `json:"score"` - CreatedAt string `json:"createdAt"` -} - //------------------------------------------------------------------------------------------------------------ // QuestionOutputReq 获取题目列表请求 @@ -93,6 +85,7 @@ type QuestionOutputRes struct { ErrorRate int `json:"errorRate"` } +// QuestionUpdateReq 修改题目请求 type QuestionUpdateReq struct { Id int `json:"id"` Stem string `json:"stem"` @@ -104,6 +97,37 @@ type QuestionUpdateReq struct { QuestionTypeId string `json:"question_type_id"` CourseRecommendationId string `json:"course+recommendation_id"` } + +// QuestionDelReq 删除题目请求 type QuestionDelReq struct { Id int `json:"id"` } + +// UserScoreOutput 用户成绩输出 +type UserScoreOutput struct { + Id int `json:"id"` + UserName string `json:"user_name"` + UserIdentity string `json:"user_identity"` + Jwcode int `json:"jwcode"` + Score int `json:"score"` + CreatedAt string `json:"createdAt"` +} + +// 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"` +} + +// ErrorOutPutUserReq 获取错题用户列表请求 +type ErrorOutPutUserReq struct { + Id int `json:"id"` +} +type ErrorOutPutUserRes struct { + UserName string `json:"user_name"` + UserIdentity string `json:"user_identity"` + ErrorCount int `json:"error_count"` +} diff --git a/Knowledge_Test_Go/go.mod b/Knowledge_Test_Go/go.mod index 393a3d2..58cc845 100644 --- a/Knowledge_Test_Go/go.mod +++ b/Knowledge_Test_Go/go.mod @@ -1,11 +1,12 @@ module Knowledge_Test_Go -go 1.23.0 +go 1.24.0 require ( github.com/gogf/gf/contrib/drivers/mysql/v2 v2.9.4 github.com/gogf/gf/contrib/nosql/redis/v2 v2.9.4 github.com/gogf/gf/v2 v2.9.4 + github.com/xuri/excelize/v2 v2.10.0 ) require ( @@ -30,14 +31,20 @@ require ( github.com/olekukonko/ll v0.0.9 // indirect github.com/olekukonko/tablewriter v1.1.0 // indirect github.com/redis/go-redis/v9 v9.12.1 // indirect + github.com/richardlehane/mscfb v1.0.4 // indirect + github.com/richardlehane/msoleps v1.0.4 // indirect github.com/rivo/uniseg v0.4.7 // indirect + github.com/tiendc/go-deepcopy v1.7.1 // indirect + github.com/xuri/efp v0.0.1 // indirect + github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/otel v1.38.0 // indirect go.opentelemetry.io/otel/metric v1.38.0 // indirect go.opentelemetry.io/otel/sdk v1.38.0 // indirect go.opentelemetry.io/otel/trace v1.38.0 // indirect - golang.org/x/net v0.43.0 // indirect - golang.org/x/sys v0.35.0 // indirect - golang.org/x/text v0.28.0 // indirect + golang.org/x/crypto v0.43.0 // indirect + golang.org/x/net v0.46.0 // indirect + golang.org/x/sys v0.37.0 // indirect + golang.org/x/text v0.30.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/Knowledge_Test_Go/go.sum b/Knowledge_Test_Go/go.sum index 2fe1044..c03b0fe 100644 --- a/Knowledge_Test_Go/go.sum +++ b/Knowledge_Test_Go/go.sum @@ -61,6 +61,11 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/redis/go-redis/v9 v9.12.1 h1:k5iquqv27aBtnTm2tIkROUDp8JBXhXZIVu1InSgvovg= github.com/redis/go-redis/v9 v9.12.1/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= +github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM= +github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk= +github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= +github.com/richardlehane/msoleps v1.0.4 h1:WuESlvhX3gH2IHcd8UqyCuFY5yiq/GR/yqaSM/9/g00= +github.com/richardlehane/msoleps v1.0.4/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= @@ -68,6 +73,14 @@ github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/tiendc/go-deepcopy v1.7.1 h1:LnubftI6nYaaMOcaz0LphzwraqN8jiWTwm416sitff4= +github.com/tiendc/go-deepcopy v1.7.1/go.mod h1:4bKjNC2r7boYOkD2IOuZpYjmlDdzjbpTRyCx+goBCJQ= +github.com/xuri/efp v0.0.1 h1:fws5Rv3myXyYni8uwj2qKjVaRP30PdjeYe2Y6FDsCL8= +github.com/xuri/efp v0.0.1/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI= +github.com/xuri/excelize/v2 v2.10.0 h1:8aKsP7JD39iKLc6dH5Tw3dgV3sPRh8uRVXu/fMstfW4= +github.com/xuri/excelize/v2 v2.10.0/go.mod h1:SC5TzhQkaOsTWpANfm+7bJCldzcnU/jrhqkTi/iBHBU= +github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 h1:+C0TIdyyYmzadGaL/HBLbf3WdLgC29pgyhTjAT/0nuE= +github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= @@ -82,13 +95,17 @@ go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJr go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= -golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= +golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= +golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= +golang.org/x/image v0.25.0 h1:Y6uW6rH1y5y/LK1J8BPWZtr6yZ7hrsy6hFrXjgsc2fQ= +golang.org/x/image v0.25.0/go.mod h1:tCAmOEGthTtkalusGp1g3xa2gke8J6c2N565dTyl9Rs= +golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= +golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= -golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= +golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= +golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/Knowledge_Test_Go/internal/cmd/cmd.go b/Knowledge_Test_Go/internal/cmd/cmd.go index 477128e..3b966b9 100644 --- a/Knowledge_Test_Go/internal/cmd/cmd.go +++ b/Knowledge_Test_Go/internal/cmd/cmd.go @@ -34,16 +34,12 @@ var ( group.Middleware( ghttp.MiddlewareHandlerResponse, ) - group.POST("/questions/get", controller.NewQuestionBank().GetQuestions) - group.POST("/questions/update", controller.NewQuestionBank().QuestionUpdate) - group.POST("/questions/del", controller.NewQuestionBank().QuestionDel) - //kt := controller.NewKnowledgeTest() - //qb := controller.NewQuestionBank() - - //group.GET("/users", kt.AdminUserList) - //group.GET("/wrong-statistics", kt.AdminWrongStatistics) - //group.GET("/questions", qb.AdminQuestionList) - //group.POST("/questions/update", qb.UpdateQuestion) + group.POST("/questions/get", controller.NewQuestionBank().GetQuestions) // 获取题目列表 + group.POST("/questions/update", controller.NewQuestionBank().QuestionUpdate) // 新增修改题目 + group.POST("/questions/del", controller.NewQuestionBank().QuestionDel) // 删除题目 + group.POST("/user/score", controller.NewQuestionBank().UserScoreOutput) // 获取用户成绩 + group.POST("/questions/user", controller.NewQuestionBank().ErrorOutPutUser) // 获取错题用户列表 + group.POST("/questions/export", controller.NewQuestionBank().ExportQuestion) // 导出题目数据到 Excel }) s.Run() diff --git a/Knowledge_Test_Go/internal/controller/questionBank.go b/Knowledge_Test_Go/internal/controller/questionBank.go index 2472e9b..e50e657 100644 --- a/Knowledge_Test_Go/internal/controller/questionBank.go +++ b/Knowledge_Test_Go/internal/controller/questionBank.go @@ -48,6 +48,7 @@ func (c *cQuestionBank) QuestionUpdate(r *ghttp.Request) { }) } +// QuestionDel 删除题目 func (c *cQuestionBank) QuestionDel(r *ghttp.Request) { ctx := r.GetCtx() var req *v1.QuestionDelReq @@ -62,3 +63,47 @@ func (c *cQuestionBank) QuestionDel(r *ghttp.Request) { "list": res, }) } + +// UserScoreOutput 获取用户成绩列表 +func (c *cQuestionBank) UserScoreOutput(r *ghttp.Request) { + ctx := r.GetCtx() + var req *v1.UserScoreOutputReq + if err := r.Parse(&req); err != nil { + response.JsonExit(r, 400, err.Error()) + } + res, total, err := service.QuestionBank().UserScoreOutput(ctx, req) + if err != nil { + response.JsonExit(r, 400, err.Error()) + } + response.JsonExit(r, 200, "success", g.Map{ + "list": res, + "total": total, + }) +} + +// ErrorOutPutUser 错题统计出错用户 +func (c *cQuestionBank) ErrorOutPutUser(r *ghttp.Request) { + ctx := r.GetCtx() + var req *v1.ErrorOutPutUserReq + if err := r.Parse(&req); err != nil { + response.JsonExit(r, 400, err.Error()) + } + res, total, err := service.QuestionBank().ErrorOutPutUser(ctx, req) + if err != nil { + response.JsonExit(r, 400, err.Error()) + } + response.JsonExit(r, 200, "success", g.Map{ + "list": res, + "total": total, + }) +} + +// ExportQuestion 导出题目数据到 Excel +func (c *cQuestionBank) ExportQuestion(r *ghttp.Request) { + ctx := r.GetCtx() + var req *v1.QuestionOutputReq + if err := r.Parse(&req); err != nil { + response.JsonExit(r, 400, err.Error()) + } + service.QuestionBank().ExportQuestion(r, ctx, req) +} diff --git a/Knowledge_Test_Go/internal/logic/knowledge/knowledge.go b/Knowledge_Test_Go/internal/logic/knowledge/knowledge.go index c146a60..c8c0eeb 100644 --- a/Knowledge_Test_Go/internal/logic/knowledge/knowledge.go +++ b/Knowledge_Test_Go/internal/logic/knowledge/knowledge.go @@ -21,12 +21,13 @@ func init() { // GetQuestions 获取题目列表 func (s *sKnowledgeTest) GetQuestions(ctx context.Context, req *v1.GetQuestionsReq) (res []*v1.GetQuestionsRes, total int, err error) { - db := g.Model("question_bank a").Fields("a.id", "a.stem", "a.a", "a.b", "a.c", "a.d") + db := g.Model("question_bank a").Where("isdel=0").Fields("a.id", "a.stem", "a.a", "a.b", "a.c", "a.d") err = db.Page(req.Page, req.PageSize).ScanAndCount(&res, &total, false) return } // ----------------------------------------------------------------------------------------------------------------------------------------------- + // GetUserScores 获取用户成绩 func (s *sKnowledgeTest) GetUserScores(ctx context.Context, req *v1.GetUserScoresReq) (res []*v1.GetUserScoresRes, err error) { err = g.Model("total_score").Fields("score").Where("jwcode=?", req.Jwcode).Ctx(ctx).Scan(&res) @@ -36,6 +37,8 @@ func (s *sKnowledgeTest) GetUserScores(ctx context.Context, req *v1.GetUserScore return res, nil } +// ----------------------------------------------------------------------------------------------------------------------------------------------- + // SubmitAnswers 提交答案并计算分数 func (s *sKnowledgeTest) SubmitAnswers(ctx context.Context, req *v1.SubmitAnswersReq) (*v1.SubmitAnswersRes, error) { if len(req.Answers) == 0 { @@ -47,7 +50,6 @@ func (s *sKnowledgeTest) SubmitAnswers(ctx context.Context, req *v1.SubmitAnswer for i, answer := range req.Answers { questionIds[i] = answer.QuestionId } - // 批量查询题目 var questions []*do.QuestionBank err := dao.QuestionBank.Ctx(ctx). @@ -150,10 +152,12 @@ func (s *sKnowledgeTest) SubmitAnswers(ctx context.Context, req *v1.SubmitAnswer }, nil } +// ----------------------------------------------------------------------------------------------------------------------------------------------- + // GetWrongQuestions 获取错题列表 func (s *sKnowledgeTest) GetWrongQuestions(ctx context.Context, req *v1.GetWrongQuestionsReq) (res []*v1.GetWrongQuestionsRes, total int, err error) { db := g.Model("question_record b"). - LeftJoin("question_bank a", "a.id=b.question_bank_id"). + RightJoin("question_bank a", "a.id=b.question_bank_id"). Where("b.jwcode=?", req.Jwcode).Fields("a.id", "a.stem", "a.a", "a.b", "a.c", "a.d", "a.correct_answer", "b.user_answer") err = db.Page(req.Page, req.PageSize).ScanAndCount(&res, &total, false) return diff --git a/Knowledge_Test_Go/internal/logic/questionBank/questionBank.go b/Knowledge_Test_Go/internal/logic/questionBank/questionBank.go index e88e3e5..8e719f8 100644 --- a/Knowledge_Test_Go/internal/logic/questionBank/questionBank.go +++ b/Knowledge_Test_Go/internal/logic/questionBank/questionBank.go @@ -3,10 +3,16 @@ package questionBank import ( v1 "Knowledge_Test_Go/api/v1" "Knowledge_Test_Go/internal/service" + "Knowledge_Test_Go/utility/response" "context" "fmt" + "strconv" + "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/net/ghttp" + + "github.com/xuri/excelize/v2" ) type sQuestionBank struct{} @@ -19,7 +25,7 @@ func init() { func (s *sQuestionBank) GetQuestions(ctx context.Context, req *v1.QuestionOutputReq) (res []*v1.QuestionOutputRes, total int, err error) { db := g.Model("question_bank a"). LeftJoin("question_type b", "a.question_type_id = b.id"). - LeftJoin("course_recommend c", "a.course_recommendation_id = c.id"). + LeftJoin("course_recommend c", "a.course_recommendation_id = c.id").Where("a.isdel = 0"). Fields("a.id", "a.stem", "a.a", "a.b", "a.c", "a.d", "a.correct_answer", "b.question_type_name", "c.cr_name", "a.error_count", "a.citation_count") @@ -115,3 +121,147 @@ 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) { + db := g.Model("user a"). + RightJoin("total_score b", "a.jwcode=b.jwcode"). + Fields("a.id", "a.user_name", "a.user_identity", "a.jwcode", "b.created_at", "b.score") + err = db.Page(req.Page, req.PageSize).ScanAndCount(&res, &total, false) + return +} + +//------------------------------------------------------------------------------------------------------------------------------- + +// ErrorOutPutUser 错题统计出错用户 +func (s *sQuestionBank) ErrorOutPutUser(ctx context.Context, req *v1.ErrorOutPutUserReq) (res []*v1.ErrorOutPutUserRes, total int, err error) { + // 参数验证 + if req.Id == 0 { + return nil, 0, gerror.New("题目ID不能为空") + } + + // 直接在数据库层面按 jwcode 分组统计 + err = g.Model("question_record r"). + LeftJoin("user u", "r.jwcode = u.jwcode"). + Where("r.question_bank_id = ?", req.Id). + Fields("r.jwcode", "u.user_name", "u.user_identity", "COUNT(*) as error_count"). + Group("r.jwcode", "u.user_name", "u.user_identity"). + OrderDesc("error_count"). + Scan(&res) + + if err != nil { + return nil, 0, gerror.Wrap(err, "查询错题用户统计失败") + } + + total = len(res) + return res, total, nil +} + +// ------------------------------------------------------------------------------------------------------------------------------- + +// ExportQuestion 导出题目数据到 Excel +func (s *sQuestionBank) ExportQuestion(r *ghttp.Request, ctx context.Context, req *v1.QuestionOutputReq) { + // === 1. 数据查询和验证 === + var result []*v1.QuestionOutputRes + + // 查询总数 + _, total, err := service.QuestionBank().GetQuestions(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, _ := s.GetQuestions(ctx, req) + result = append(result, res[:]...) + num += 1000 + if num > total { + break + } + req.Page++ + } + + // === 3. 创建 Excel 文件 === + excelFile := excelize.NewFile() + sheetName := "Sheet1" + fileName := "题目数据导出.xlsx" + + // === 4. 设置表头 === + headers := []string{"ID", "题干", "选项A", "选项B", "选项C", "选项D", "正确答案", "题目类型", "推荐课程", "引用次数", "错误次数", "错误率"} + for i, header := range headers { + col := string('A' + i) + excelFile.SetCellValue(sheetName, col+"1", header) + } + + // === 5. 写入数据 === + rowIndex := 2 + for _, question := range result { + excelFile.SetCellValue(sheetName, "A"+strconv.Itoa(rowIndex), rowIndex-1) + excelFile.SetCellValue(sheetName, "B"+strconv.Itoa(rowIndex), question.Id) + excelFile.SetCellValue(sheetName, "C"+strconv.Itoa(rowIndex), question.Stem) + excelFile.SetCellValue(sheetName, "D"+strconv.Itoa(rowIndex), question.A) + excelFile.SetCellValue(sheetName, "E"+strconv.Itoa(rowIndex), question.B) + excelFile.SetCellValue(sheetName, "F"+strconv.Itoa(rowIndex), question.C) + excelFile.SetCellValue(sheetName, "G"+strconv.Itoa(rowIndex), question.D) + excelFile.SetCellValue(sheetName, "H"+strconv.Itoa(rowIndex), question.CorrectAnswer) + excelFile.SetCellValue(sheetName, "I"+strconv.Itoa(rowIndex), question.QuestionTypeName) + excelFile.SetCellValue(sheetName, "J"+strconv.Itoa(rowIndex), question.CrName) + excelFile.SetCellValue(sheetName, "K"+strconv.Itoa(rowIndex), question.CitationCount) + excelFile.SetCellValue(sheetName, "L"+strconv.Itoa(rowIndex), question.ErrorCount) + excelFile.SetCellValue(sheetName, "M"+strconv.Itoa(rowIndex), strconv.Itoa(question.ErrorRate)+"%") + + 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", "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) // 统计信息 + + // === 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()) +} diff --git a/Knowledge_Test_Go/internal/service/knowledge.go b/Knowledge_Test_Go/internal/service/knowledge.go index c9d98f7..2ad0bab 100644 --- a/Knowledge_Test_Go/internal/service/knowledge.go +++ b/Knowledge_Test_Go/internal/service/knowledge.go @@ -14,7 +14,6 @@ type ( IKnowledgeTest interface { // GetQuestions 获取题目列表 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) // SubmitAnswers 提交答案并计算分数 diff --git a/Knowledge_Test_Go/internal/service/question_bank.go b/Knowledge_Test_Go/internal/service/question_bank.go index dae048f..c366b9c 100644 --- a/Knowledge_Test_Go/internal/service/question_bank.go +++ b/Knowledge_Test_Go/internal/service/question_bank.go @@ -8,6 +8,8 @@ package service import ( v1 "Knowledge_Test_Go/api/v1" "context" + + "github.com/gogf/gf/v2/net/ghttp" ) type ( @@ -18,6 +20,12 @@ type ( QuestionUpdate(ctx context.Context, req *v1.QuestionUpdateReq) (res string, err error) // 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) + // 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) } )