236 lines
7.6 KiB
Go
236 lines
7.6 KiB
Go
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
|
||
}
|