package grpc_server import ( "context" "encoding/json" "errors" "fmt" "git.hlsq.asia/mmorpg/service-common/db/redis" "git.hlsq.asia/mmorpg/service-common/log" "git.hlsq.asia/mmorpg/service-common/net/http/http_resp" "git.hlsq.asia/mmorpg/service-common/proto/rs/grpc_pb" "git.hlsq.asia/mmorpg/service-common/utils" "git.hlsq.asia/mmorpg/service-qgdzs/internal/ai" "git.hlsq.asia/mmorpg/service-qgdzs/internal/dao/model" "git.hlsq.asia/mmorpg/service-qgdzs/internal/dao/repository" "gorm.io/gorm" "time" ) var prompt = []string{` 你是一个微信小游戏的内容策划,我需要生成“每日趣味题”,要求: - 避免敏感、争议或超纲内容 - 确保选项有迷惑性,解析有趣 - 绝对要避免重复,现在的时间是:%v - 这些是我目前的分类:%v - 输出格式为 JSON 数组,不要任何解释、不要Markdown、不要任何其他字符: [{ "question": "题目文本", // 简洁,30字以内 "options": ["A. 选项1", "B. 选项2", "C. 选项3", "D. 选项4"], // 提供4个选项(A/B/C/D),其中仅1个正确 "answer": "C", // 答案 "explanation": "解析文本", // 200字以内,尽量幽默有趣 "category": "分类", // 尽量从上述分类中选择,你也可以增加,但是命名风格要类似 "difficulty": 100, // 难度分 0 - 100 }] 如果你准备好了,请回答”好的“,不要有其他任何字符 `, ` 请帮我生成 %v 道,只允许回答 JSON 数组: `, ` 请继续生成 %v 道,只允许回答 JSON 数组: `, ` 请继续生成 %v 道,只允许回答 JSON 数组: `, ` 请继续生成 %v 道,只允许回答 JSON 数组: `, ` 请继续生成 %v 道,只允许回答 JSON 数组: `} type Question struct { Question string `json:"question"` // 题干 Options []string `json:"options"` // 选项 Answer string `json:"answer"` // 答案 Explanation string `json:"explanation"` // 解析 Category string `json:"category"` // 分类 Difficulty int32 `json:"difficulty"` // 难度分 } func (s *Server) GenerateQuestion(ctx context.Context, req *grpc_pb.GenerateQuestionReq) (*grpc_pb.GenerateQuestionResp, error) { categoryDao := repository.NewCategoryDao(ctx, redis.GetCacheClient()) category, err := categoryDao.FindAll() if err != nil { return nil, utils.ErrorsWrap(err) } question := make([]*Question, 0) err = ai.NewAIClient(false, "", 0.9). RequestAI( []string{ fmt.Sprintf(prompt[0], time.Now().Format("2006-01-02 15:04:05"), category), fmt.Sprintf(prompt[1], req.Num, req.Category), fmt.Sprintf(prompt[2], req.Num), fmt.Sprintf(prompt[3], req.Num), fmt.Sprintf(prompt[4], req.Num), fmt.Sprintf(prompt[5], req.Num), }, func(content string, i int) error { if i == 0 { return nil } step := make([]*Question, 0) if err = json.Unmarshal([]byte(content), &step); err != nil { return utils.ErrorsWrapF(err, "data: %v", content) } question = append(question, step...) return nil }, ) if err != nil { return nil, utils.ErrorsWrap(err) } questionDao := repository.NewQuestionDao(ctx, redis.GetCacheClient()) for _, q := range question { marshal, _ := json.Marshal(q.Options) categorySn, err := categoryDao.FindSnByName(q.Category) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { cat, err := categoryDao.Create(&model.Category{ Category: q.Category, }) if err != nil { log.Errorf("GenerateQuestion CreateCategory error: %v", err) continue } categorySn = cat.Sn } else { log.Errorf("GenerateQuestion FindSnByName error: %v", err) continue } } if _, err = questionDao.Create(&model.Question{ Question: q.Question, Options: string(marshal), Answer: q.Answer, Explanation: q.Explanation, CategorySn: categorySn, Difficulty: q.Difficulty, }); err != nil { return nil, utils.ErrorsWrap(err) } } return nil, nil } // GetQuestion 获取题目 func (s *Server) GetQuestion(ctx context.Context, req *grpc_pb.GetQuestionReq) (*grpc_pb.GetQuestionResp, error) { question, err := repository.NewQuestionDao(ctx).FindByRandom(req.CategorySn) if err != nil { return nil, utils.ErrorsWrap(err) } options := make([]string, 0) if err = json.Unmarshal([]byte(question.Options), &options); err != nil { return nil, utils.ErrorsWrapF(err, "data: %v", question.Options) } category, _ := repository.NewCategoryDao(ctx).FindNameBySn(question.CategorySn) return &grpc_pb.GetQuestionResp{ Sn: question.Sn, Question: question.Question, Options: options, Category: category, Difficulty: question.Difficulty, }, nil } // GetQuestionInfo 获取具体的题目 func (s *Server) GetQuestionInfo(ctx context.Context, req *grpc_pb.GetQuestionInfoReq) (*grpc_pb.GetQuestionInfoResp, error) { question, err := repository.NewQuestionDao(ctx).FindBySn(req.QuestionSn) if err != nil { return nil, utils.ErrorsWrap(err) } options := make([]string, 0) if err = json.Unmarshal([]byte(question.Options), &options); err != nil { return nil, utils.ErrorsWrapF(err, "data: %v", question.Options) } category, _ := repository.NewCategoryDao(ctx).FindNameBySn(question.CategorySn) return &grpc_pb.GetQuestionInfoResp{ Question: question.Question, Options: options, Category: category, Difficulty: question.Difficulty, Explanation: question.Explanation, }, nil } // AnswerQuestion 回答题目 func (s *Server) AnswerQuestion(ctx context.Context, req *grpc_pb.AnswerQuestionReq) (*grpc_pb.AnswerQuestionResp, error) { question, err := repository.NewQuestionDao(ctx).FindBySn(req.Sn) if err != nil { return nil, utils.ErrorsWrap(err) } options := make([]string, 0) if err = json.Unmarshal([]byte(question.Options), &options); err != nil { return nil, utils.ErrorsWrapF(err, "data: %v", question.Options) } // 保存答题记录 if utils.ShouldBindUsn(ctx, &req.USN) { isCorrect := int32(0) if req.Answer == question.Answer { isCorrect = 1 } _, _ = repository.NewRecordDao(ctx).Create(&model.Record{ UserSn: req.USN, QuestionSn: question.Sn, Answer: req.Answer, IsCorrect: isCorrect, }) } return &grpc_pb.AnswerQuestionResp{ Answer: question.Answer, Explanation: question.Explanation, }, nil } // GetAllCategory 获取所有类目 func (s *Server) GetAllCategory(ctx context.Context, req *grpc_pb.GetAllCategoryReq) (*grpc_pb.GetAllCategoryResp, error) { categoryList, err := repository.NewCategoryDao(ctx).FindAll() if err != nil { return nil, utils.ErrorsWrap(err) } categories := make([]*grpc_pb.GetAllCategoryItem, 0) for _, category := range categoryList { categories = append(categories, &grpc_pb.GetAllCategoryItem{ Sn: category.Sn, Category: category.Category, }) } return &grpc_pb.GetAllCategoryResp{ Categories: categories, }, nil } // GetRecord 获取答题记录 func (s *Server) GetRecord(ctx context.Context, req *grpc_pb.GetRecordReq) (*grpc_pb.GetRecordResp, error) { if !utils.ShouldBindUsn(ctx, &req.USN) { return nil, http_resp.ParamError } records, count, err := repository.NewRecordDao(ctx).FindByUSN(req.USN, int(req.Page), int(req.PageSize)) if err != nil { return nil, utils.ErrorsWrap(err) } resp := make([]*grpc_pb.GetRecordItem, 0) for _, record := range records { resp = append(resp, &grpc_pb.GetRecordItem{ QuestionSn: record.QuestionSn, Question: record.Question, Difficulty: record.Difficulty, Category: record.Category, QuestionAnswer: record.QuestionAnswer, Answer: record.Answer, CreateTime: record.CreatedAt.Unix(), }) } return &grpc_pb.GetRecordResp{ Count: int32(count), Records: resp, }, nil }