yunzer_go/server/services/exam.go

260 lines
7.9 KiB
Go

package services
import (
"errors"
"time"
"server/models"
"github.com/beego/beego/v2/client/orm"
)
type QuestionListParams struct {
TenantId int
Keyword string
QuestionType *int8
BankId int64
Page int
PageSize int
}
// CountQuestionsInBank 统计指定题库下的题目数量(仅统计未删除且启用的题目)
func CountQuestionsInBank(tenantId int, bankId int64) (int64, error) {
o := orm.NewOrm()
qs := o.QueryTable(new(models.ExamQuestion)).Filter("delete_time__isnull", true).Filter("status", 1).Filter("bank_id", bankId)
if tenantId > 0 {
qs = qs.Filter("tenant_id", tenantId)
}
return qs.Count()
}
type QuestionWithMeta struct {
Question *models.ExamQuestion
Options []*models.ExamQuestionOption
Answer *models.ExamQuestionAnswer
}
type BankListParams struct {
TenantId int
Keyword string
Page int
PageSize int
}
func GetExamQuestions(params QuestionListParams) ([]*models.ExamQuestion, int64, error) {
o := orm.NewOrm()
qs := o.QueryTable(new(models.ExamQuestion)).Filter("delete_time__isnull", true).Filter("status", 1)
if params.TenantId > 0 {
qs = qs.Filter("tenant_id", params.TenantId)
}
if params.Keyword != "" {
qs = qs.Filter("question_title__icontains", params.Keyword)
}
if params.QuestionType != nil {
qs = qs.Filter("question_type", *params.QuestionType)
}
if params.BankId > 0 {
qs = qs.Filter("bank_id", params.BankId)
}
total, err := qs.Count()
if err != nil {
return nil, 0, err
}
if params.Page <= 0 {
params.Page = 1
}
if params.PageSize <= 0 {
params.PageSize = 10
}
offset := (params.Page - 1) * params.PageSize
var list []*models.ExamQuestion
_, err = qs.OrderBy("-id").Limit(params.PageSize, offset).All(&list, "Id", "TenantId", "QuestionType", "QuestionTitle", "Score")
return list, total, err
}
func GetExamQuestionDetail(tenantId int, id int64) (*QuestionWithMeta, error) {
o := orm.NewOrm()
q := new(models.ExamQuestion)
if err := o.QueryTable(q).Filter("id", id).Filter("delete_time__isnull", true).One(q); err != nil {
return nil, err
}
if tenantId > 0 && q.TenantId != tenantId {
return nil, errors.New("not found")
}
var opts []*models.ExamQuestionOption
_, _ = o.QueryTable(new(models.ExamQuestionOption)).Filter("question_id", q.Id).Filter("delete_time__isnull", true).All(&opts)
a := new(models.ExamQuestionAnswer)
_ = o.QueryTable(a).Filter("question_id", q.Id).Filter("delete_time__isnull", true).One(a)
return &QuestionWithMeta{Question: q, Options: opts, Answer: a}, nil
}
// FindExamQuestionByTitle 在指定租户下根据题目标题精确查找试题
// 用于批量导入时判断是否存在完全相同题目,以便执行覆盖更新
func FindExamQuestionByTitle(tenantId int, title string) (*models.ExamQuestion, error) {
o := orm.NewOrm()
q := new(models.ExamQuestion)
qs := o.QueryTable(q).Filter("delete_time__isnull", true).Filter("question_title", title)
if tenantId > 0 {
qs = qs.Filter("tenant_id", tenantId)
}
if err := qs.One(q); err != nil {
return nil, err
}
return q, nil
}
func CreateExamQuestion(tenantId int, q *models.ExamQuestion, options []models.ExamQuestionOption, answerContent string) (int64, error) {
o := orm.NewOrm()
if tenantId > 0 {
q.TenantId = tenantId
}
q.Status = 1
if _, err := o.Insert(q); err != nil {
return 0, err
}
for i := range options {
options[i].TenantId = q.TenantId
options[i].QuestionId = q.Id
_, _ = o.Insert(&options[i])
}
ans := &models.ExamQuestionAnswer{TenantId: q.TenantId, QuestionId: q.Id, AnswerContent: answerContent}
_, _ = o.Insert(ans)
return q.Id, nil
}
func UpdateExamQuestion(tenantId int, id int64, q *models.ExamQuestion, options []models.ExamQuestionOption, answerContent string) error {
o := orm.NewOrm()
existing := models.ExamQuestion{Id: id}
if err := o.Read(&existing); err != nil {
return err
}
if tenantId > 0 && existing.TenantId != tenantId {
return errors.New("not found")
}
existing.QuestionType = q.QuestionType
existing.QuestionTitle = q.QuestionTitle
existing.QuestionAnalysis = q.QuestionAnalysis
existing.Score = q.Score
// 可选更新题库归属
fields := []string{"QuestionType", "QuestionTitle", "QuestionAnalysis", "Score"}
if q.BankId > 0 {
existing.BankId = q.BankId
fields = append(fields, "BankId")
}
if _, err := o.Update(&existing, fields...); err != nil {
return err
}
// soft delete old options and answer
now := time.Now()
o.QueryTable(new(models.ExamQuestionOption)).Filter("question_id", id).Filter("delete_time__isnull", true).Update(orm.Params{"delete_time": now})
o.QueryTable(new(models.ExamQuestionAnswer)).Filter("question_id", id).Filter("delete_time__isnull", true).Update(orm.Params{"delete_time": now})
for i := range options {
options[i].TenantId = existing.TenantId
options[i].QuestionId = id
_, _ = o.Insert(&options[i])
}
ans := &models.ExamQuestionAnswer{TenantId: existing.TenantId, QuestionId: id, AnswerContent: answerContent}
_, _ = o.Insert(ans)
return nil
}
func DeleteExamQuestion(tenantId int, id int64) error {
o := orm.NewOrm()
existing := models.ExamQuestion{Id: id}
if err := o.Read(&existing); err != nil {
return err
}
if tenantId > 0 && existing.TenantId != tenantId {
return errors.New("not found")
}
now := time.Now()
if _, err := o.QueryTable(new(models.ExamQuestion)).Filter("id", id).Update(orm.Params{"delete_time": now}); err != nil {
return err
}
o.QueryTable(new(models.ExamQuestionOption)).Filter("question_id", id).Filter("delete_time__isnull", true).Update(orm.Params{"delete_time": now})
o.QueryTable(new(models.ExamQuestionAnswer)).Filter("question_id", id).Filter("delete_time__isnull", true).Update(orm.Params{"delete_time": now})
return nil
}
func GetExamQuestionBanks(params BankListParams) ([]*models.ExamQuestionBank, int64, error) {
o := orm.NewOrm()
qs := o.QueryTable(new(models.ExamQuestionBank)).Filter("delete_time__isnull", true).Filter("status", 1)
if params.TenantId > 0 {
qs = qs.Filter("tenant_id", params.TenantId)
}
if params.Keyword != "" {
qs = qs.Filter("bank_name__icontains", params.Keyword)
}
total, err := qs.Count()
if err != nil {
return nil, 0, err
}
if params.Page <= 0 {
params.Page = 1
}
if params.PageSize <= 0 {
params.PageSize = 10
}
offset := (params.Page - 1) * params.PageSize
var list []*models.ExamQuestionBank
_, err = qs.OrderBy("-id").Limit(params.PageSize, offset).All(&list, "Id", "TenantId", "BankName", "BankDesc")
return list, total, err
}
func GetExamQuestionBankDetail(tenantId int, id int64) (*models.ExamQuestionBank, error) {
o := orm.NewOrm()
bank := new(models.ExamQuestionBank)
if err := o.QueryTable(bank).Filter("id", id).Filter("delete_time__isnull", true).One(bank); err != nil {
return nil, err
}
if tenantId > 0 && bank.TenantId != tenantId {
return nil, errors.New("not found")
}
return bank, nil
}
func CreateExamQuestionBank(tenantId int, bank *models.ExamQuestionBank) (int64, error) {
o := orm.NewOrm()
if tenantId > 0 {
bank.TenantId = tenantId
}
bank.Status = 1
if _, err := o.Insert(bank); err != nil {
return 0, err
}
return bank.Id, nil
}
func UpdateExamQuestionBank(tenantId int, id int64, bank *models.ExamQuestionBank) error {
o := orm.NewOrm()
existing := models.ExamQuestionBank{Id: id}
if err := o.Read(&existing); err != nil {
return err
}
if tenantId > 0 && existing.TenantId != tenantId {
return errors.New("not found")
}
existing.BankName = bank.BankName
existing.BankDesc = bank.BankDesc
if _, err := o.Update(&existing, "BankName", "BankDesc"); err != nil {
return err
}
return nil
}
func DeleteExamQuestionBank(tenantId int, id int64) error {
o := orm.NewOrm()
existing := models.ExamQuestionBank{Id: id}
if err := o.Read(&existing); err != nil {
return err
}
if tenantId > 0 && existing.TenantId != tenantId {
return errors.New("not found")
}
now := time.Now()
if _, err := o.QueryTable(new(models.ExamQuestionBank)).Filter("id", id).Update(orm.Params{"delete_time": now}); err != nil {
return err
}
return nil
}