fonchain-fiee/pkg/service/exam/question.go

378 lines
9.4 KiB
Go
Raw Normal View History

2025-02-19 06:24:15 +00:00
package exam
import (
"context"
"fmt"
"io"
"os"
"strconv"
"net/http"
"strings"
"errors"
"sort"
"unicode"
"dubbo.apache.org/dubbo-go/v3/common/logger"
"github.com/360EntSecGroup-Skylar/excelize"
"github.com/fonchain_enterprise/fonchain-main/api/exam"
"github.com/fonchain_enterprise/fonchain-main/pkg/e"
"github.com/fonchain_enterprise/fonchain-main/pkg/model/login"
"github.com/fonchain_enterprise/fonchain-main/pkg/serializer"
"github.com/fonchain_enterprise/fonchain-main/pkg/service"
"github.com/gin-gonic/gin"
)
func SaveQuestion(c *gin.Context) {
req := new(exam.QuestionInfo)
if err := c.ShouldBind(req); err != nil {
logger.Errorf("SaveQuestion ShouldBind err", err)
service.ResponseMsg(c, e.SUCCESS, serializer.Response{
Msg: err.Error(),
Status: e.Failed,
})
return
}
userInfo := login.GetUserInfoFromC(c)
logger.Info("-------------------------------")
logger.Info("userInfo:", userInfo)
req.CreaterName = userInfo.NickName
req.CreaterId = strconv.FormatUint(userInfo.ID, 10)
_, err := service.GrpcExamClientImpl.SaveQuestion(context.Background(), req)
if err != nil {
service.Error(c, e.Error, err)
return
}
service.Success(c)
}
func GetQuestionInfo(c *gin.Context) {
req := new(exam.InfoRequest)
if err := c.ShouldBind(req); err != nil {
logger.Errorf("GetQuestionInfo ShouldBind err", err)
service.ResponseMsg(c, e.SUCCESS, serializer.Response{
Msg: err.Error(),
Status: e.Failed,
})
return
}
// userInfo := login.GetUserInfoFromC(c)
// logger.Info("-------------------------------")
// logger.Info("userInfo:", userInfo)
// req.UserUUID = strconv.FormatUint(userInfo.ID, 10)
res, err := service.GrpcExamClientImpl.GetQuestionInfo(context.Background(), req)
if err != nil {
service.Error(c, e.Error, err)
return
}
service.Success(c, res)
}
func CheckQuestion(c *gin.Context) {
req := new(exam.InfoRequest)
if err := c.ShouldBind(req); err != nil {
logger.Errorf("CheckQuestion ShouldBind err", err)
service.ResponseMsg(c, e.SUCCESS, serializer.Response{
Msg: err.Error(),
Status: e.Failed,
})
return
}
res, err := service.GrpcExamClientImpl.CheckQuestion(context.Background(), req)
if err != nil {
service.Error(c, e.Error, err)
return
}
service.Success(c, res)
}
func UpdateQuestion(c *gin.Context) {
req := new(exam.QuestionInfo)
if err := c.ShouldBind(req); err != nil {
logger.Errorf("UpdateQuestion ShouldBind err", err)
service.ResponseMsg(c, e.SUCCESS, serializer.Response{
Msg: err.Error(),
Status: e.Failed,
})
return
}
userInfo := login.GetUserInfoFromC(c)
logger.Info("-------------------------------")
logger.Info("userInfo:", userInfo)
req.UpdaterName = userInfo.NickName
req.UpdaterID = int32(userInfo.ID)
res, err := service.GrpcExamClientImpl.UpdateQuestion(context.Background(), req)
if err != nil {
service.Error(c, e.Error, err)
return
}
service.Success(c, res)
}
func DeleteQuestion(c *gin.Context) {
req := new(exam.QuestionInfo)
if err := c.ShouldBind(req); err != nil {
logger.Errorf("DeleteQuestion ShouldBind err", err)
service.ResponseMsg(c, e.SUCCESS, serializer.Response{
Msg: err.Error(),
Status: e.Failed,
})
return
}
res, err := service.GrpcExamClientImpl.DeleteQuestion(context.Background(), req)
logger.Info("=============", err)
if err != nil {
service.Error(c, e.Error, err)
return
}
service.Success(c, res)
}
func DetailQuestion(c *gin.Context) {
req := new(exam.InfoRequest)
if err := c.ShouldBind(req); err != nil {
logger.Errorf("DetailQuestion ShouldBind err", err)
service.ResponseMsg(c, e.SUCCESS, serializer.Response{
Msg: err.Error(),
Status: e.Failed,
})
return
}
res, err := service.GrpcExamClientImpl.DetailQuestion(context.Background(), req)
logger.Info("=============", err)
if err != nil {
service.Error(c, e.Error, err)
return
}
service.Success(c, res)
}
func ImportQuestions(c *gin.Context) {
// 从请求中获取上传的文件
file, header, err := c.Request.FormFile("file")
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Failed to get uploaded file"})
return
}
fmt.Println("=========================")
fmt.Println(header.Filename)
if !strings.HasSuffix(header.Filename, ".xlsx") {
c.JSON(http.StatusBadRequest, gin.H{"error": "Wrong file to upload"})
return
}
defer file.Close()
// 创建一个临时文件来存储上传的文件
tmpFile, err := os.CreateTemp("", "imported_data_person_monitor_*.xlsx")
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create temporary file"})
return
}
defer tmpFile.Close()
defer os.Remove(tmpFile.Name())
// 将上传的文件复制到临时文件
_, err = io.Copy(tmpFile, file)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to copy file to temporary location"})
return
}
// 使用 excelize 打开临时文件
xlsxFile, err := excelize.OpenFile(tmpFile.Name())
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to open Excel file"})
return
}
// 获取第一个工作表
sheetName := xlsxFile.GetSheetName(1)
rows := xlsxFile.GetRows(sheetName)
req := new(exam.ImportQuestionRequest)
if err := c.ShouldBind(req); err != nil {
logger.Errorf("ImportQuestions ShouldBind err", err)
service.ResponseMsg(c, e.SUCCESS, serializer.Response{
Msg: err.Error(),
Status: e.Failed,
})
return
}
userInfo := login.GetUserInfoFromC(c)
logger.Info("-------------------------------")
logger.Info("userInfo:", userInfo)
req.FileName = header.Filename
req.CreaterName = userInfo.NickName
req.CreaterID = int64(userInfo.ID)
req.QuestionInfos = make([]*exam.QuestionInfo, 0)
questionTypeList := []string{"单选题", "多选题", "判断题", "填空题", "简答题"}
selectTypeList := []string{"单选题", "多选题", "判断题"}
for i, row := range rows {
line := strconv.FormatInt(int64(i+1), 10)
// 忽略表头行
if i <= 1 {
continue
}
if !IsInSlice(row[0], questionTypeList) {
service.Error(c, e.Error, errors.New("第"+line+"行题型不合法"))
return
}
if row[2] == "" {
service.Error(c, e.Error, errors.New("第"+line+"行题目名称不能为空"))
return
}
if row[4] == "" {
service.Error(c, e.Error, errors.New("第"+line+"行正确答案不能为空"))
return
}
var answer = make([]string, 0)
if IsInSlice(row[0], selectTypeList) {
if !strings.HasSuffix(row[3], "。") {
service.Error(c, e.Error, errors.New("第"+line+"行答案选项没有以中文句号结尾"))
return
}
if !CheckIfAllUpperCaseLetters(row[4]) {
service.Error(c, e.Error, errors.New("第"+line+"行正确答案中不是全部大写的字母"))
return
}
if row[0] == "多选题" {
tempAnswer := CleanAndSortString(row[4])
if len(tempAnswer) > 1 {
for _, v := range tempAnswer {
answer = append(answer, string(v))
}
} else {
service.Error(c, e.Error, errors.New("第"+line+"行多选题,答案不是多个"))
return
}
} else {
tempAnswer := CleanAndSortString(row[4])
for _, v := range tempAnswer {
answer = append(answer, string(v))
}
}
} else {
if row[0] == "填空题" {
if strings.Contains(row[4], "、") {
fillParts := strings.Split(row[4], "、")
for _, part := range fillParts {
answer = append(answer, part)
}
} else {
answer = append(answer, row[4])
}
} else {
answer = append(answer, row[4])
}
}
options := make([]*exam.QuestionOptionInfo, 0)
if !IsInSlice(row[0], selectTypeList) {
if row[0] == "填空题" {
fillParts := strings.Split(row[4], "、")
for i, _ := range fillParts {
questionOptionInfo := new(exam.QuestionOptionInfo)
questionOptionInfo.OptionId = fmt.Sprintf("%d", i+1)
questionOptionInfo.OptionName = ""
options = append(options, questionOptionInfo)
}
} else {
options = nil
}
} else {
parts := strings.Split(row[3], "。")
// 最后一个没有例子A.北京市。B.上海市。C.天津市。D重庆市。
for i := 0; i < len(parts)-1; i++ {
moreParts := strings.Split(parts[i], ".")
if len(moreParts) != 2 {
service.Error(c, e.Error, errors.New("第"+line+"行答案选项不合法"))
return
}
questionOptionInfo := new(exam.QuestionOptionInfo)
questionOptionInfo.OptionId = moreParts[0]
questionOptionInfo.OptionName = moreParts[1]
options = append(options, questionOptionInfo)
}
}
questionInfo := exam.QuestionInfo{
QuestionType: row[0],
ClassifyName: row[1],
Topic: row[2],
Option: options,
Answer: answer,
}
req.QuestionInfos = append(req.QuestionInfos, &questionInfo)
}
res, err := service.GrpcExamClientImpl.ImportQuestions(context.Background(), req)
logger.Info("=============", err)
if err != nil {
service.Error(c, e.Error, err)
return
}
service.Success(c, res)
}
func IsInSlice(needle string, haystack []string) bool {
for _, item := range haystack {
if item == needle {
return true
}
}
return false
}
func CheckIfAllUpperCaseLetters(s string) bool {
for _, r := range s {
if !unicode.IsUpper(r) {
return false
}
}
return true
}
func CleanAndSortString(s string) string {
charMap := make(map[rune]bool)
var result []rune
for _, r := range s {
if _, exists := charMap[r]; !exists {
charMap[r] = true
result = append(result, r)
}
}
sort.Slice(result, func(i, j int) bool {
return result[i] < result[j]
})
return string(result)
}