This commit is contained in:
wucongxing8150 2024-07-02 17:37:50 +08:00
parent c87c7076df
commit 53e72e1ee2
35 changed files with 1813 additions and 60 deletions

View File

@ -3,8 +3,12 @@ package controller
// Api api接口
type Api struct {
Migrate
Public // 公共方法-不验证权限
AdminUser // 后台用户
Question // 题目
QuestionQa // 问答题库
Public // 公共方法-不验证权限
AdminUser // 后台用户
Question // 题目
QuestionQa // 问答题库
QuestionQaItem // 问答题库明细
Label // 标签
Static // 统计
Share // 分享
}

112
api/controller/label.go Normal file
View File

@ -0,0 +1,112 @@
package controller
import (
"github.com/gin-gonic/gin"
"knowledge/api/dao"
"knowledge/api/dto"
"knowledge/api/requests"
"knowledge/api/responses"
"knowledge/api/service"
"knowledge/global"
"knowledge/utils"
"strconv"
)
type Label struct{}
// GetLabelList 获取标签列表
func (r *Label) GetLabelList(c *gin.Context) {
labelRequest := requests.LabelRequest{}
req := labelRequest.GetLabelList
if err := c.ShouldBind(&req); err != nil {
responses.FailWithMessage(err.Error(), c)
return
}
// 参数验证
if err := global.Validate.Struct(req); err != nil {
responses.FailWithMessage(utils.Translate(err), c)
return
}
labelDao := dao.LabelDao{}
label, err := labelDao.GetLabelListSearch(req)
if err != nil {
responses.FailWithMessage(err.Error(), c)
return
}
// 处理返回值
GetLabelListResponses := dto.GetLabelListDto(label)
if err != nil {
responses.FailWithMessage(err.Error(), c)
return
}
responses.OkWithData(GetLabelListResponses, c)
}
// AddLabel 新增标签
func (r *Label) AddLabel(c *gin.Context) {
labelRequest := requests.LabelRequest{}
req := labelRequest.AddLabel
if err := c.ShouldBindJSON(&req); err != nil {
responses.FailWithMessage(err.Error(), c)
return
}
// 参数验证
if err := global.Validate.Struct(req); err != nil {
responses.FailWithMessage(utils.Translate(err), c)
return
}
// 业务处理
labelService := service.LabelService{}
_, err := labelService.AddLabel(req)
if err != nil {
responses.FailWithMessage(err.Error(), c)
return
}
responses.Ok(c)
}
// PutLabel 修改标签
func (r *Label) PutLabel(c *gin.Context) {
labelRequest := requests.LabelRequest{}
req := labelRequest.PutLabel
if err := c.ShouldBindJSON(&req); err != nil {
responses.FailWithMessage(err.Error(), c)
return
}
// 参数验证
if err := global.Validate.Struct(req); err != nil {
responses.FailWithMessage(utils.Translate(err), c)
return
}
id := c.Param("label_id")
if id == "" {
responses.FailWithMessage("缺少参数", c)
return
}
// 将 id 转换为 int64 类型
labelId, err := strconv.ParseInt(id, 10, 64)
if err != nil {
responses.Fail(c)
return
}
// 业务处理
labelService := service.LabelService{}
_, err = labelService.PutLabel(labelId, req)
if err != nil {
responses.FailWithMessage(err.Error(), c)
return
}
responses.Ok(c)
}

View File

@ -59,6 +59,32 @@ func (r *Question) GetQuestionPage(c *gin.Context) {
responses.OkWithData(result, c)
}
// GetQuestion 获取题目详情
func (r *Question) GetQuestion(c *gin.Context) {
id := c.Param("question_id")
if id == "" {
responses.FailWithMessage("缺少参数", c)
return
}
// 将 id 转换为 int64 类型
questionId, err := strconv.ParseInt(id, 10, 64)
if err != nil {
responses.Fail(c)
return
}
// 业务处理
questionService := service.QuestionService{}
getQuestionResponses, err := questionService.GetQuestion(questionId)
if err != nil {
responses.FailWithMessage(err.Error(), c)
return
}
responses.OkWithData(getQuestionResponses, c)
}
// AddQuestion 新增题目
func (r *Question) AddQuestion(c *gin.Context) {
questionRequest := requests.QuestionRequest{}
@ -191,8 +217,8 @@ func (r *Question) PutQuestionTest(c *gin.Context) {
// DeleteQuestion 删除题目
func (r *Question) DeleteQuestion(c *gin.Context) {
couponRequest := requests.QuestionRequest{}
req := couponRequest.DeleteQuestion
questionRequest := requests.QuestionRequest{}
req := questionRequest.DeleteQuestion
if err := c.ShouldBindJSON(&req); err != nil {
responses.FailWithMessage(err.Error(), c)
return
@ -214,3 +240,28 @@ func (r *Question) DeleteQuestion(c *gin.Context) {
responses.Ok(c)
}
// GetQuestionCount 获取题目数量
func (r *Question) GetQuestionCount(c *gin.Context) {
questionRequest := requests.QuestionRequest{}
req := questionRequest.GetQuestionCount
if err := c.ShouldBind(&req); err != nil {
responses.FailWithMessage(err.Error(), c)
return
}
// 参数验证
if err := global.Validate.Struct(req); err != nil {
responses.FailWithMessage(utils.Translate(err), c)
return
}
questionDao := dao.QuestionDao{}
total, err := questionDao.GetQuestionCountSearch(req)
if err != nil {
responses.FailWithMessage(err.Error(), c)
return
}
responses.OkWithData(total, c)
}

View File

@ -94,7 +94,7 @@ func (r *QuestionQa) AddQuestionQa(c *gin.Context) {
}
// PutQuestionQa 修改问答题库
func (r *Question) PutQuestionQa(c *gin.Context) {
func (r *QuestionQa) PutQuestionQa(c *gin.Context) {
questionQaRequest := requests.QuestionQaRequest{}
req := questionQaRequest.PutQuestionQa
if err := c.ShouldBindJSON(&req); err != nil {
@ -141,7 +141,7 @@ func (r *Question) PutQuestionQa(c *gin.Context) {
}
// PutQuestionQaPassword 修改问答题库密码
func (r *Question) PutQuestionQaPassword(c *gin.Context) {
func (r *QuestionQa) PutQuestionQaPassword(c *gin.Context) {
questionQaRequest := requests.QuestionQaRequest{}
req := questionQaRequest.PutQuestionQaPassword
if err := c.ShouldBindJSON(&req); err != nil {
@ -180,7 +180,7 @@ func (r *Question) PutQuestionQaPassword(c *gin.Context) {
}
// PutQuestionQaExpire 修改问答题库有效期
func (r *Question) PutQuestionQaExpire(c *gin.Context) {
func (r *QuestionQa) PutQuestionQaExpire(c *gin.Context) {
questionQaRequest := requests.QuestionQaRequest{}
req := questionQaRequest.PutQuestionQaExpire
if err := c.ShouldBindJSON(&req); err != nil {
@ -217,3 +217,68 @@ func (r *Question) PutQuestionQaExpire(c *gin.Context) {
responses.Ok(c)
}
// PutQuestionQaRule 修改问答题库规则解释
func (r *QuestionQa) PutQuestionQaRule(c *gin.Context) {
questionQaRequest := requests.QuestionQaRequest{}
req := questionQaRequest.PutQuestionQaRule
if err := c.ShouldBindJSON(&req); err != nil {
responses.FailWithMessage(err.Error(), c)
return
}
// 参数验证
if err := global.Validate.Struct(req); err != nil {
responses.FailWithMessage(utils.Translate(err), c)
return
}
id := c.Param("qa_id")
if id == "" {
responses.FailWithMessage("缺少参数", c)
return
}
// 将 id 转换为 int64 类型
qaId, err := strconv.ParseInt(id, 10, 64)
if err != nil {
responses.Fail(c)
return
}
// 业务处理
questionQaService := service.QuestionQaService{}
_, err = questionQaService.PutQuestionQaRule(qaId, req)
if err != nil {
responses.FailWithMessage(err.Error(), c)
return
}
responses.Ok(c)
}
// GetQuestionQa 获取问答题库详情
func (r *QuestionQa) GetQuestionQa(c *gin.Context) {
id := c.Param("qa_id")
if id == "" {
responses.FailWithMessage("缺少参数", c)
return
}
// 将 id 转换为 int64 类型
qaId, err := strconv.ParseInt(id, 10, 64)
if err != nil {
responses.Fail(c)
return
}
// 业务处理
questionQaService := service.QuestionQaService{}
getQuestionQaResponses, err := questionQaService.GetQuestionQa(qaId)
if err != nil {
responses.FailWithMessage(err.Error(), c)
return
}
responses.OkWithData(getQuestionQaResponses, c)
}

View File

@ -0,0 +1,125 @@
package controller
import (
"github.com/gin-gonic/gin"
"knowledge/api/dao"
"knowledge/api/dto"
"knowledge/api/requests"
"knowledge/api/responses"
"knowledge/api/service"
"knowledge/global"
"knowledge/utils"
"strconv"
)
type QuestionQaItem struct{}
// GetQuestionQaItemPage 获取问答题库明细列表-分页
func (r *QuestionQaItem) GetQuestionQaItemPage(c *gin.Context) {
questionQaItemRequest := requests.QuestionQaItemRequest{}
req := questionQaItemRequest.GetQuestionQaItemPage
if err := c.ShouldBindJSON(&req); err != nil {
responses.FailWithMessage(err.Error(), c)
return
}
// 参数验证
if err := global.Validate.Struct(req); err != nil {
responses.FailWithMessage(utils.Translate(err), c)
return
}
if req.Page == 0 {
req.Page = 1
}
if req.PageSize == 0 {
req.PageSize = 20
}
questionQaItemDao := dao.QuestionQaItemDao{}
questionQaItem, total, err := questionQaItemDao.GetQuestionQaItemPageSearch(req, req.Page, req.PageSize)
if err != nil {
responses.FailWithMessage(err.Error(), c)
return
}
// 处理返回值
questionQaItemPageResponses := dto.GetQuestionQaItemListDto(questionQaItem)
if err != nil {
responses.FailWithMessage(err.Error(), c)
return
}
result := make(map[string]interface{})
result["page"] = req.Page
result["page_size"] = req.PageSize
result["total"] = total
result["data"] = questionQaItemPageResponses
responses.OkWithData(result, c)
}
// DeleteQuestionQaItem 删除问答题库明细
func (r *QuestionQaItem) DeleteQuestionQaItem(c *gin.Context) {
questionQaItemRequest := requests.QuestionQaItemRequest{}
req := questionQaItemRequest.DeleteQuestionQaItem
if err := c.ShouldBindJSON(&req); err != nil {
responses.FailWithMessage(err.Error(), c)
return
}
// 参数验证
if err := global.Validate.Struct(req); err != nil {
responses.FailWithMessage(utils.Translate(err), c)
return
}
// 业务处理
questionQaItemService := service.QuestionQaItemService{}
_, err := questionQaItemService.DeleteQuestionQaItem(req)
if err != nil {
responses.FailWithMessage(err.Error(), c)
return
}
responses.Ok(c)
}
// PutQuestionQaItemMust 修改必选
func (r *QuestionQaItem) PutQuestionQaItemMust(c *gin.Context) {
questionQaItemRequest := requests.QuestionQaItemRequest{}
req := questionQaItemRequest.PutQuestionQaItemMust
if err := c.ShouldBindJSON(&req); err != nil {
responses.FailWithMessage(err.Error(), c)
return
}
// 参数验证
if err := global.Validate.Struct(req); err != nil {
responses.FailWithMessage(utils.Translate(err), c)
return
}
id := c.Param("item_id")
if id == "" {
responses.FailWithMessage("缺少参数", c)
return
}
// 将 id 转换为 int64 类型
itemId, err := strconv.ParseInt(id, 10, 64)
if err != nil {
responses.Fail(c)
return
}
// 业务处理
questionQaItemService := service.QuestionQaItemService{}
_, err = questionQaItemService.PutQuestionQaItemMust(itemId, req)
if err != nil {
responses.FailWithMessage(err.Error(), c)
return
}
responses.Ok(c)
}

38
api/controller/share.go Normal file
View File

@ -0,0 +1,38 @@
package controller
import (
"github.com/gin-gonic/gin"
"knowledge/api/requests"
"knowledge/api/responses"
"knowledge/api/service"
"knowledge/global"
"knowledge/utils"
)
type Share struct{}
// GetShare 获取分享数据
func (r *Share) GetShare(c *gin.Context) {
shareRequest := requests.ShareRequest{}
req := shareRequest.GetShare
if err := c.ShouldBind(&req); err != nil {
responses.FailWithMessage(err.Error(), c)
return
}
// 参数验证
if err := global.Validate.Struct(req); err != nil {
responses.FailWithMessage(utils.Translate(err), c)
return
}
// 业务处理
shareService := service.ShareService{}
getShareResponses, err := shareService.GetShare(req)
if err != nil {
responses.FailWithMessage(err.Error(), c)
return
}
responses.OkWithData(getShareResponses, c)
}

22
api/controller/static.go Normal file
View File

@ -0,0 +1,22 @@
package controller
import (
"github.com/gin-gonic/gin"
"knowledge/api/responses"
"knowledge/api/service"
)
type Static struct{}
// GetStatic 获取统计数据
func (r *Static) GetStatic(c *gin.Context) {
// 业务处理
staticService := service.StaticService{}
getStaticResponses, err := staticService.GetStatic()
if err != nil {
responses.FailWithMessage(err.Error(), c)
return
}
responses.OkWithData(getStaticResponses, c)
}

View File

@ -3,6 +3,7 @@ package dao
import (
"gorm.io/gorm"
"knowledge/api/model"
"knowledge/api/requests"
"knowledge/global"
)
@ -63,6 +64,17 @@ func (r *LabelDao) GetLabelList(maps interface{}) (m []*model.Label, err error)
return m, nil
}
// GetLabelCount 获取数量
func (r *LabelDao) GetLabelCount(maps interface{}) (total int64, err error) {
var totalRecords int64
err = global.Db.Model(&model.Label{}).Where(maps).Count(&totalRecords).Error
if err != nil {
return 0, err
}
return totalRecords, nil
}
// AddLabel 新增
func (r *LabelDao) AddLabel(tx *gorm.DB, model *model.Label) (*model.Label, error) {
if err := tx.Create(model).Error; err != nil {
@ -79,3 +91,38 @@ func (r *LabelDao) GetLabel(maps interface{}) (m *model.Label, err error) {
}
return m, nil
}
// GetLabelListSearch 获取列表
func (r *LabelDao) GetLabelListSearch(req requests.GetLabelList) (m []*model.Label, err error) {
// 构建查询条件
query := global.Db.Model(&model.Label{})
// 主键id
if req.LabelId != "" {
query = query.Where("label_id = ?", req.LabelId)
}
// 标签名称
if req.LabelName != "" {
query = query.Where("label_name LIKE ?", "%"+req.LabelName+"%")
}
// 父级ID
if req.ParentId != "" {
query = query.Where("parent_id = ?", req.ParentId)
}
// 级别
if req.LabelLevel != nil {
query = query.Where("label_level = ?", req.LabelLevel)
}
// 排序
query = query.Order("created_at desc")
err = query.Find(&m).Error
if err != nil {
return nil, err
}
return m, nil
}

View File

@ -3,6 +3,7 @@ package dao
import (
"errors"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"knowledge/api/model"
"knowledge/api/requests"
"knowledge/global"
@ -22,6 +23,15 @@ func (r *QuestionDao) GetQuestionById(questionId int64) (m *model.Question, err
return m, nil
}
// GetQuestionPreloadById 获取数据-加载全部关联-id
func (r *QuestionDao) GetQuestionPreloadById(questionId int64) (m *model.Question, err error) {
err = global.Db.Preload(clause.Associations).First(&m, questionId).Error
if err != nil {
return nil, err
}
return m, nil
}
// DeleteQuestion 删除
func (r *QuestionDao) DeleteQuestion(tx *gorm.DB, maps interface{}) error {
err := tx.Where(maps).Delete(&model.Question{}).Error
@ -77,7 +87,7 @@ func (r *QuestionDao) GetQuestionCount(maps interface{}) (total int64, err error
// GetQuestionListRand 获取列表-随机
func (r *QuestionDao) GetQuestionListRand(maps interface{}, limit int) (m []*model.Question, err error) {
err = global.Db.Where(maps).Find(&m).Order(gorm.Expr("RAND()")).Limit(limit).Error
err = global.Db.Where(maps).Order("rand()").Limit(limit).Find(&m).Error
if err != nil {
return nil, err
}
@ -205,3 +215,49 @@ func (r *QuestionDao) GetQuestionPageSearch(req requests.GetQuestionPage, page,
}
return m, totalRecords, nil
}
// GetQuestionCountSearch 获取题目数量
func (r *QuestionDao) GetQuestionCountSearch(req requests.GetQuestionCount) (total int64, err error) {
var totalRecords int64
// 构建查询条件
query := global.Db.Model(&model.Question{})
// 题目类型
query = query.Where("question_type = ?", req.QuestionType)
// 状态
if req.QuestionStatus != nil {
query = query.Where("question_status = ?", req.QuestionStatus)
}
// 难度
if req.Difficulty != nil {
query = query.Where("difficulty = ?", req.Difficulty)
}
// 题目来源
if req.QuestionSource != nil {
query = query.Where("question_source = ?", req.QuestionSource)
}
// 一级标签id
if req.FirstLabelId != nil {
query = query.Where("first_label_id = ?", req.FirstLabelId)
}
// 二级标签id
if req.SecondLabelId != nil {
query = query.Where("second_label_id = ?", req.SecondLabelId)
}
// 排序
query = query.Order("created_at desc")
// 查询总数量
if err := query.Count(&totalRecords).Error; err != nil {
return 0, err
}
return totalRecords, nil
}

View File

@ -1,8 +1,10 @@
package dao
import (
"errors"
"gorm.io/gorm"
"knowledge/api/model"
"knowledge/api/requests"
"knowledge/global"
)
@ -10,8 +12,17 @@ type QuestionQaItemDao struct {
}
// GetQuestionQaItemById 获取数据-id
func (r *QuestionQaItemDao) GetQuestionQaItemById(qaId int64) (m *model.QuestionQaItem, err error) {
err = global.Db.First(&m, qaId).Error
func (r *QuestionQaItemDao) GetQuestionQaItemById(itemId int64) (m *model.QuestionQaItem, err error) {
err = global.Db.First(&m, itemId).Error
if err != nil {
return nil, err
}
return m, nil
}
// GetQuestionQaItemListByQaId 获取数据-QaId
func (r *QuestionQaItemDao) GetQuestionQaItemListByQaId(qaId int64) (m []*model.QuestionQaItem, err error) {
err = global.Db.Where("qa_id = ?", qaId).Find(&m).Error
if err != nil {
return nil, err
}
@ -27,6 +38,14 @@ func (r *QuestionQaItemDao) DeleteQuestionQaItem(tx *gorm.DB, maps interface{})
return nil
}
// DeleteQuestionQaItemById 删除-Id
func (r *QuestionQaItemDao) DeleteQuestionQaItemById(tx *gorm.DB, itemId int64) error {
if err := tx.Delete(&model.QuestionQaItem{}, itemId).Error; err != nil {
return err
}
return nil
}
// DeleteQuestionQaItemByQaId 删除-QaId
func (r *QuestionQaItemDao) DeleteQuestionQaItemByQaId(tx *gorm.DB, qaId int64) error {
if err := tx.Where("qa_id = ?", qaId).Delete(&model.QuestionQaItem{}).Error; err != nil {
@ -44,6 +63,15 @@ func (r *QuestionQaItemDao) EditQuestionQaItem(tx *gorm.DB, maps interface{}, da
return nil
}
// EditQuestionQaItemById 修改-id
func (r *QuestionQaItemDao) EditQuestionQaItemById(tx *gorm.DB, itemId int64, data interface{}) error {
err := tx.Model(&model.QuestionQaItem{}).Where("item_id = ?", itemId).Updates(data).Error
if err != nil {
return err
}
return nil
}
// GetQuestionQaItemList 获取列表
func (r *QuestionQaItemDao) GetQuestionQaItemList(maps interface{}) (m []*model.QuestionQaItem, err error) {
err = global.Db.Where(maps).Find(&m).Error
@ -53,6 +81,15 @@ func (r *QuestionQaItemDao) GetQuestionQaItemList(maps interface{}) (m []*model.
return m, nil
}
// GetQuestionQaItemListRand 获取列表-随机
func (r *QuestionQaItemDao) GetQuestionQaItemListRand(maps interface{}, limit int) (m []*model.QuestionQaItem, err error) {
err = global.Db.Where(maps).Limit(limit).Order("rand()").Find(&m).Error
if err != nil {
return nil, err
}
return m, nil
}
// AddQuestionQaItem 新增
func (r *QuestionQaItemDao) AddQuestionQaItem(tx *gorm.DB, model *model.QuestionQaItem) (*model.QuestionQaItem, error) {
if err := tx.Create(model).Error; err != nil {
@ -69,3 +106,118 @@ func (r *QuestionQaItemDao) GetQuestionQaItem(maps interface{}) (m *model.Questi
}
return m, nil
}
// GetQuestionQaItemPageSearch 获取列表-分页
func (r *QuestionQaItemDao) GetQuestionQaItemPageSearch(req requests.GetQuestionQaItemPage, page, pageSize int) (m []*model.QuestionQaItem, total int64, err error) {
var totalRecords int64
// 构建查询条件
query := global.Db.Model(&model.QuestionQaItem{})
// 题目表
query = query.Preload("Question", func(db *gorm.DB) *gorm.DB {
return db.Select("question_id", "question_name", "question_type", "question_source", "difficulty", "first_label_id", "second_label_id")
})
// 一级标签
query = query.Preload("Question.FirstLabel", func(db *gorm.DB) *gorm.DB {
return db.Select("label_id", "label_name")
})
// 二级标签
query = query.Preload("Question.SecondLabel", func(db *gorm.DB) *gorm.DB {
return db.Select("label_id", "label_name")
})
// 题库id
query = query.Where("qa_id = ?", req.QaId)
// 题目id
if req.QuestionId != "" {
query = query.Where("question_id = ?", req.QuestionId)
}
// 是否必被选中
if req.IsMustSelect != nil {
query = query.Where("is_must_select = ?", req.IsMustSelect)
}
// 题目名称
if req.QuestionName != "" {
subQuery := global.Db.Model(&model.Question{}).
Select("question_id").
Where("question_name LIKE ?", "%"+req.QuestionName+"%")
query = query.Where(gorm.Expr("question_id IN (?)", subQuery))
}
// 题目类型
if req.QuestionType != nil {
subQuery := global.Db.Model(&model.Question{}).
Select("question_id").
Where("question_type = ?", req.QuestionType)
query = query.Where(gorm.Expr("question_id IN (?)", subQuery))
}
// 题目来源
if req.QuestionSource != nil {
subQuery := global.Db.Model(&model.Question{}).
Select("question_id").
Where("question_source = ?", req.QuestionSource)
query = query.Where(gorm.Expr("question_id IN (?)", subQuery))
}
// 难度
if req.Difficulty != nil {
subQuery := global.Db.Model(&model.Question{}).
Select("question_id").
Where("difficulty = ?", req.Difficulty)
query = query.Where(gorm.Expr("question_id IN (?)", subQuery))
}
// 一级标签id
if req.FirstLabelId != nil {
subQuery := global.Db.Model(&model.Question{}).
Select("question_id").
Where("first_label_id = ?", req.FirstLabelId)
query = query.Where(gorm.Expr("question_id IN (?)", subQuery))
}
// 二级标签id
if req.SecondLabelId != nil {
subQuery := global.Db.Model(&model.Question{}).
Select("question_id").
Where("second_label_id = ?", req.SecondLabelId)
query = query.Where(gorm.Expr("question_id IN (?)", subQuery))
}
// 排序
if req.Order != nil {
if req.Order.UpdatedAt != "" {
if req.Order.UpdatedAt != "desc" && req.Order.UpdatedAt != "asc" {
return nil, 0, errors.New("排序字段错误")
}
query = query.Order("updated_at " + req.Order.UpdatedAt)
}
}
// 排序
query = query.Order("created_at desc")
// 查询总数量
if err := query.Count(&totalRecords).Error; err != nil {
return nil, 0, err
}
err = query.Scopes(model.Paginate(page, pageSize)).Find(&m).Error
if err != nil {
return nil, 0, err
}
return m, totalRecords, nil
}

View File

@ -3,30 +3,44 @@ package dto
import (
"fmt"
"knowledge/api/model"
"knowledge/utils"
"strings"
)
// QuestionDto 题目表(单选-多选-问答-判断)
type QuestionDto struct {
QuestionId string `json:"question_id"` // 主键id
QuestionName string `json:"question_name"` // 题目名称
QuestionType int `json:"question_type"` // 题目类型(1:单选 2:多选 3:问答 4:判断)
QuestionStatus int `json:"question_status"` // 状态1:正常 2:禁用)
IsDelete int `json:"is_delete"` // 是否删除0:否 1:是)
QuestionSource int `json:"question_source"` // 题目来源1:本题库 2:外部数据)
QuestionImage []string `json:"question_image"` // 题目图片(逗号分隔)
QuestionAnswer string `json:"question_answer"` // 答案
QuestionAnalysis string `json:"question_analysis"` // 解析
Difficulty int `json:"difficulty"` // 难度0:未知 1:低 2:中 3:高)
FirstLabelId string `json:"first_label_id"` // 一级标签id
SecondLabelId string `json:"second_label_id"` // 二级标签id
CreatedAt model.LocalTime `json:"created_at"` // 创建时间
UpdatedAt model.LocalTime `json:"updated_at"` // 更新时间
FirstLabel *LabelDto `json:"first_label"` // 一级标签
SecondLabel *LabelDto `json:"second_label"` // 二级标签
QuestionId string `json:"question_id"` // 主键id
QuestionName string `json:"question_name"` // 题目名称
QuestionType int `json:"question_type"` // 题目类型(1:单选 2:多选 3:问答 4:判断)
QuestionStatus int `json:"question_status"` // 状态1:正常 2:禁用)
IsDelete int `json:"is_delete"` // 是否删除0:否 1:是)
QuestionSource int `json:"question_source"` // 题目来源1:本题库 2:外部数据)
QuestionImage []string `json:"question_image"` // 题目图片(逗号分隔)
QuestionAnswer string `json:"question_answer"` // 答案
QuestionAnalysis string `json:"question_analysis"` // 解析
Difficulty int `json:"difficulty"` // 难度0:未知 1:低 2:中 3:高)
FirstLabelId string `json:"first_label_id"` // 一级标签id
SecondLabelId string `json:"second_label_id"` // 二级标签id
CreatedAt model.LocalTime `json:"created_at"` // 创建时间
UpdatedAt model.LocalTime `json:"updated_at"` // 更新时间
FirstLabel *LabelDto `json:"first_label"` // 一级标签
SecondLabel *LabelDto `json:"second_label"` // 二级标签
QuestionOption []*QuestionOptionDto `json:"question_option"` // 选项
}
// GetQuestionDto 题目详情
func GetQuestionDto(m *model.Question) *QuestionDto {
var questionImage []string
if m.QuestionImage != "" {
result := strings.Split(m.QuestionImage, ",")
if len(result) > 0 {
for _, v := range result {
v = utils.AddOssDomain(v)
questionImage = append(questionImage, v)
}
}
}
return &QuestionDto{
QuestionId: fmt.Sprintf("%d", m.QuestionId),
QuestionName: m.QuestionName,
@ -34,6 +48,7 @@ func GetQuestionDto(m *model.Question) *QuestionDto {
QuestionStatus: m.QuestionStatus,
IsDelete: m.IsDelete,
QuestionSource: m.QuestionSource,
QuestionImage: questionImage,
QuestionAnswer: m.QuestionAnswer,
QuestionAnalysis: m.QuestionAnalysis,
Difficulty: m.Difficulty,
@ -51,6 +66,17 @@ func GetQuestionListDto(m []*model.Question) []*QuestionDto {
if len(m) > 0 {
for i, v := range m {
var questionImage []string
if v.QuestionImage != "" {
result := strings.Split(v.QuestionImage, ",")
if len(result) > 0 {
for _, v := range result {
v = utils.AddOssDomain(v)
questionImage = append(questionImage, v)
}
}
}
response := &QuestionDto{
QuestionId: fmt.Sprintf("%d", v.QuestionId),
QuestionName: v.QuestionName,
@ -58,6 +84,7 @@ func GetQuestionListDto(m []*model.Question) []*QuestionDto {
QuestionStatus: v.QuestionStatus,
IsDelete: v.IsDelete,
QuestionSource: v.QuestionSource,
QuestionImage: questionImage,
QuestionAnswer: v.QuestionAnswer,
QuestionAnalysis: v.QuestionAnalysis,
Difficulty: v.Difficulty,
@ -100,3 +127,11 @@ func (r *QuestionDto) LoadSecondLabel(m *model.Label) *QuestionDto {
}
return r
}
// LoadQuestionOption 加载选项
func (r *QuestionDto) LoadQuestionOption(m []*model.QuestionOption) *QuestionDto {
if len(m) > 0 {
r.QuestionOption = GetQuestionOptionListDto(m)
}
return r
}

51
api/dto/QuestionOption.go Normal file
View File

@ -0,0 +1,51 @@
package dto
import (
"fmt"
"knowledge/api/model"
)
// QuestionOptionDto 题目选项表
type QuestionOptionDto struct {
OptionId string `json:"option_id"` // 主键id
QuestionId string `json:"question_id"` // 题目id
OptionValue string `json:"option_value"` // 选项内容
CreatedAt model.LocalTime `json:"created_at"` // 创建时间
UpdatedAt model.LocalTime `json:"updated_at"` // 更新时间
}
// GetQuestionOptionDto 题目选项详情
func GetQuestionOptionDto(m *model.QuestionOption) *QuestionOptionDto {
return &QuestionOptionDto{
OptionId: fmt.Sprintf("%d", m.OptionId),
QuestionId: fmt.Sprintf("%d", m.QuestionId),
OptionValue: m.OptionValue,
CreatedAt: m.CreatedAt,
UpdatedAt: m.UpdatedAt,
}
}
// GetQuestionOptionListDto 题目选项列表
func GetQuestionOptionListDto(m []*model.QuestionOption) []*QuestionOptionDto {
// 处理返回值
responses := make([]*QuestionOptionDto, len(m))
if len(m) > 0 {
for i, v := range m {
response := &QuestionOptionDto{
OptionId: fmt.Sprintf("%d", v.OptionId),
QuestionId: fmt.Sprintf("%d", v.QuestionId),
OptionValue: v.OptionValue,
CreatedAt: v.CreatedAt,
UpdatedAt: v.UpdatedAt,
}
// 将转换后的结构体添加到新切片中
responses[i] = response
}
}
return responses
}

View File

@ -1,25 +1,37 @@
package dto
import (
"encoding/json"
"fmt"
"knowledge/api/model"
"knowledge/utils"
)
// QuestionQaDto 问答题库
type QuestionQaDto struct {
QaId string `json:"qa_id"` // 主键id
QaName string `json:"qa_name"` // 名称
QaQuantity int `json:"qa_quantity"` // 题目数量
QaStatus int `json:"qa_status"` // 状态1:正常 2:无效)
QaRuleContent string `json:"qa_rule_content"` // 规则解释
QaDisplayType int `json:"qa_display_type"` // 展示类型1:常规 2:飞花令)
QaExpireTime model.LocalTime `json:"qa_expire_time"` // 过期时间
QaLink string `json:"qa_link"` // 分享链接
QaPassword string `json:"qa_password"` // 分享密码
OpenNumber int `json:"open_number"` // 打开的次数
Image string `json:"image"` // 背景图
CreatedAt model.LocalTime `json:"created_at"` // 创建时间
UpdatedAt model.LocalTime `json:"updated_at"` // 更新时间
QaId string `json:"qa_id"` // 主键id
QaName string `json:"qa_name"` // 名称
QaQuantity int `json:"qa_quantity"` // 题目数量
QaStatus int `json:"qa_status"` // 状态1:正常 2:无效)
QaRuleContent string `json:"qa_rule_content"` // 规则解释
QaDisplayType int `json:"qa_display_type"` // 展示类型1:常规 2:飞花令)
QaExpireTime model.LocalTime `json:"qa_expire_time"` // 过期时间
QaShareId string `json:"qa_share_id"` // 分享标识
QaPassword string `json:"qa_password"` // 分享密码
OpenNumber int `json:"open_number"` // 打开的次数
Image string `json:"image"` // 背景图
ItemContent []ItemContentDto `json:"item_content"` // 明细选题规则json
CreatedAt model.LocalTime `json:"created_at"` // 创建时间
UpdatedAt model.LocalTime `json:"updated_at"` // 更新时间
}
// ItemContentDto 问答题库-明细选题规则
type ItemContentDto struct {
QuestionType int `json:"question_type"` // 题目类型(1:单选 2:多选 3:问答 4:判断)
FirstLabelId string `json:"first_label_id"` // 一级标签id
SecondLabelId string `json:"second_label_id"` // 二级标签id
Difficulty int `json:"difficulty"` // 难度0:未知 1:低 2:中 3:高)
Quantity int `json:"quantity"` // 数量
}
// GetQuestionQaListDto 问答题库列表
@ -37,7 +49,8 @@ func GetQuestionQaListDto(m []*model.QuestionQa) []*QuestionQaDto {
QaRuleContent: v.QaRuleContent,
QaDisplayType: v.QaDisplayType,
QaExpireTime: v.QaExpireTime,
QaLink: v.QaLink,
QaShareId: utils.AddDomain(v.QaShareId),
Image: utils.AddOssDomain(v.Image),
OpenNumber: v.OpenNumber,
CreatedAt: v.CreatedAt,
UpdatedAt: v.UpdatedAt,
@ -50,3 +63,34 @@ func GetQuestionQaListDto(m []*model.QuestionQa) []*QuestionQaDto {
return responses
}
// GetQuestionQaDto 问答题库详情
func GetQuestionQaDto(m *model.QuestionQa) *QuestionQaDto {
return &QuestionQaDto{
QaId: fmt.Sprintf("%d", m.QaId),
QaName: m.QaName,
QaQuantity: m.QaQuantity,
QaStatus: m.QaStatus,
QaRuleContent: m.QaRuleContent,
QaDisplayType: m.QaDisplayType,
QaExpireTime: m.QaExpireTime,
QaShareId: utils.AddDomain(m.QaShareId),
OpenNumber: m.OpenNumber,
Image: utils.AddOssDomain(m.Image),
CreatedAt: m.CreatedAt,
UpdatedAt: m.UpdatedAt,
}
}
// LoadItemContent 加载明细选题规则
func (r *QuestionQaDto) LoadItemContent(s string) *QuestionQaDto {
if s != "" {
var itemContentDto []ItemContentDto
err := json.Unmarshal([]byte(s), &itemContentDto)
if err == nil {
r.ItemContent = itemContentDto
}
}
return r
}

94
api/dto/QuestionQaItem.go Normal file
View File

@ -0,0 +1,94 @@
package dto
import (
"fmt"
"knowledge/api/model"
)
// QuestionQaItemDto 知识问答-明细
type QuestionQaItemDto struct {
ItemId string `json:"item_id"` // 主键id
QaId string `json:"qa_id"` // 知识问答id
QuestionId string `json:"question_id"` // 题目id
IsMustSelect int `json:"is_must_select"` // 是否必被选中0:否 1:是)
CreatedAt model.LocalTime `json:"created_at"` // 创建时间
UpdatedAt model.LocalTime `json:"updated_at"` // 更新时间
FirstLabel *LabelDto `json:"first_label"` // 一级标签
SecondLabel *LabelDto `json:"second_label"` // 二级标签
Question *QuestionDto `json:"question"` // 题目
}
// GetQuestionQaItemDto 题库明细详情
func GetQuestionQaItemDto(m *model.QuestionQaItem) *QuestionQaItemDto {
return &QuestionQaItemDto{
ItemId: fmt.Sprintf("%d", m.ItemId),
QaId: fmt.Sprintf("%d", m.QaId),
QuestionId: fmt.Sprintf("%d", m.QuestionId),
IsMustSelect: m.IsMustSelect,
CreatedAt: m.CreatedAt,
UpdatedAt: m.UpdatedAt,
}
}
// GetQuestionQaItemListDto 题目列表
func GetQuestionQaItemListDto(m []*model.QuestionQaItem) []*QuestionQaItemDto {
// 处理返回值
responses := make([]*QuestionQaItemDto, len(m))
if len(m) > 0 {
for i, v := range m {
response := &QuestionQaItemDto{
ItemId: fmt.Sprintf("%d", v.ItemId),
QaId: fmt.Sprintf("%d", v.QaId),
QuestionId: fmt.Sprintf("%d", v.QuestionId),
IsMustSelect: v.IsMustSelect,
CreatedAt: v.CreatedAt,
UpdatedAt: v.UpdatedAt,
}
// 加载题目
if v.Question != nil {
response = response.LoadQuestion(v.Question)
}
// 加载一级标签
if v.Question.FirstLabel != nil {
response = response.LoadFirstLabel(v.Question.FirstLabel)
}
// 加载二级标签
if v.Question.SecondLabel != nil {
response = response.LoadSecondLabel(v.Question.SecondLabel)
}
// 将转换后的结构体添加到新切片中
responses[i] = response
}
}
return responses
}
// LoadFirstLabel 加载一级标签
func (r *QuestionQaItemDto) LoadFirstLabel(m *model.Label) *QuestionQaItemDto {
if m != nil {
r.FirstLabel = GetLabelDto(m)
}
return r
}
// LoadSecondLabel 加载二级标签
func (r *QuestionQaItemDto) LoadSecondLabel(m *model.Label) *QuestionQaItemDto {
if m != nil {
r.SecondLabel = GetLabelDto(m)
}
return r
}
// LoadQuestion 加载题目
func (r *QuestionQaItemDto) LoadQuestion(m *model.Question) *QuestionQaItemDto {
if m != nil {
r.Question = GetQuestionDto(m)
}
return r
}

56
api/dto/Share.go Normal file
View File

@ -0,0 +1,56 @@
package dto
import (
"knowledge/api/model"
"knowledge/utils"
"strings"
)
type ShareDto struct {
QuestionQa *QuestionQaDto `json:"question_qa"` // 题库数据
Question []*ShareQuestionDto `json:"question"` // 题目数据
}
// ShareQuestionDto 题目数据
type ShareQuestionDto struct {
QuestionName string `json:"question_name"` // 题目名称
QuestionType int `json:"question_type"` // 题目类型(1:单选 2:多选 3:问答 4:判断)
QuestionSource int `json:"question_source"` // 题目来源1:本题库 2:外部数据)
QuestionImage []string `json:"question_image"` // 题目图片(逗号分隔)
QuestionAnswer string `json:"question_answer"` // 答案
QuestionAnalysis string `json:"question_analysis"` // 解析
Difficulty int `json:"difficulty"` // 难度0:未知 1:低 2:中 3:高)
IsMustSelect int `json:"is_must_select"`
}
// GetShareQuestionDto 分享题目详情
func GetShareQuestionDto(m *model.Question) *ShareQuestionDto {
var questionImage []string
if m.QuestionImage != "" {
result := strings.Split(m.QuestionImage, ",")
if len(result) > 0 {
for _, v := range result {
v = utils.AddOssDomain(v)
questionImage = append(questionImage, v)
}
}
}
return &ShareQuestionDto{
QuestionName: m.QuestionName,
QuestionType: m.QuestionType,
QuestionSource: m.QuestionSource,
QuestionImage: questionImage,
QuestionAnswer: m.QuestionAnswer,
QuestionAnalysis: m.QuestionAnalysis,
Difficulty: m.Difficulty,
}
}
// LoadQuestionQa 加载题库数据
func (r *ShareDto) LoadQuestionQa(m *model.QuestionQa) *ShareDto {
if m != nil {
r.QuestionQa = GetQuestionQaDto(m)
}
return r
}

42
api/dto/Static.go Normal file
View File

@ -0,0 +1,42 @@
package dto
import (
"fmt"
"knowledge/api/model"
"knowledge/utils"
)
type StaticDto struct {
Name string `json:"name"` // 名称
Total int `json:"total"` // 数量
}
// GetStaticListDto 问答题库列表
func GetStaticListDto(m []*model.QuestionQa) []*QuestionQaDto {
// 处理返回值
responses := make([]*QuestionQaDto, len(m))
if len(m) > 0 {
for i, v := range m {
response := &QuestionQaDto{
QaId: fmt.Sprintf("%d", v.QaId),
QaName: v.QaName,
QaQuantity: v.QaQuantity,
QaStatus: v.QaStatus,
QaRuleContent: v.QaRuleContent,
QaDisplayType: v.QaDisplayType,
QaExpireTime: v.QaExpireTime,
QaShareId: utils.AddDomain(v.QaShareId),
Image: utils.AddOssDomain(v.Image),
OpenNumber: v.OpenNumber,
CreatedAt: v.CreatedAt,
UpdatedAt: v.UpdatedAt,
}
// 将转换后的结构体添加到新切片中
responses[i] = response
}
}
return responses
}

View File

@ -21,8 +21,9 @@ type Question struct {
FirstLabelId *int64 `gorm:"column:first_label_id;type:bigint(19);comment:一级标签id" json:"first_label_id"`
SecondLabelId *int64 `gorm:"column:second_label_id;type:bigint(19);comment:二级标签id" json:"second_label_id"`
Model
FirstLabel *Label `gorm:"foreignKey:FirstLabelId;references:label_id" json:"first_label"`
SecondLabel *Label `gorm:"foreignKey:SecondLabelId;references:label_id" json:"second_label"`
FirstLabel *Label `gorm:"foreignKey:FirstLabelId;references:label_id" json:"first_label"`
SecondLabel *Label `gorm:"foreignKey:SecondLabelId;references:label_id" json:"second_label"`
QuestionOption []*QuestionOption `gorm:"foreignKey:QuestionId;references:question_id" json:"question_option"`
}
func (m *Question) TableName() string {

View File

@ -15,10 +15,11 @@ type QuestionQa struct {
QaRuleContent string `gorm:"column:qa_rule_content;type:text;comment:规则解释" json:"qa_rule_content"`
QaDisplayType int `gorm:"column:qa_display_type;type:tinyint(1);default:1;comment:展示类型1:常规 2:飞花令)" json:"qa_display_type"`
QaExpireTime LocalTime `gorm:"column:qa_expire_time;type:datetime;comment:过期时间" json:"qa_expire_time"`
QaLink string `gorm:"column:qa_link;type:varchar(255);comment:分享链接" json:"qa_link"`
QaShareId string `gorm:"column:qa_share_id;type:varchar(255);comment:分享标识" json:"qa_share_id"`
QaPassword string `gorm:"column:qa_password;type:varchar(255);comment:分享密码" json:"qa_password"`
OpenNumber int `gorm:"column:open_number;type:int(1);default:0;comment:打开的次数" json:"open_number"`
Image string `gorm:"column:image;type:varchar(255);comment:背景图" json:"image"`
ItemContent string `gorm:"column:item_content;type:text;comment:明细选题规则json" json:"item_content"`
Model
}

View File

@ -13,6 +13,8 @@ type QuestionQaItem struct {
QuestionId int64 `gorm:"column:question_id;type:bigint(19);comment:题目id;NOT NULL" json:"question_id"`
IsMustSelect int `gorm:"column:is_must_select;type:tinyint(1);default:0;comment:是否必被选中0:否 1:是)" json:"is_must_select"`
Model
Question *Question `gorm:"foreignKey:QuestionId;references:question_id" json:"question"`
QuestionQa *QuestionQa `gorm:"foreignKey:QaId;references:qa_id" json:"question_qa"`
}
func (m *QuestionQaItem) TableName() string {

27
api/requests/Label.go Normal file
View File

@ -0,0 +1,27 @@
package requests
type LabelRequest struct {
GetLabelList // 获取标签列表
AddLabel // 新增标签
PutLabel // 修改标签
}
// GetLabelList 获取标签列表
type GetLabelList struct {
LabelId string `json:"label_id" form:"label_id" label:"主键id"`
LabelName string `json:"label_name" form:"label_name" label:"标签名称"`
ParentId string `json:"parent_id" form:"parent_id" label:"父级ID"`
LabelLevel *int `json:"label_level" form:"label_level" label:"级别"`
}
// AddLabel 新增标签
type AddLabel struct {
LabelName string `json:"label_name" form:"label_name" label:"标签名称" validate:"required" `
ParentId string `json:"parent_id" form:"parent_id" label:"父级ID" `
LabelLevel int `json:"label_level" form:"label_level" label:"级别" validate:"required,number,min=1" `
}
// PutLabel 修改标签
type PutLabel struct {
LabelName string `json:"label_name" form:"label_name" label:"标签名称" validate:"required" `
}

View File

@ -1,12 +1,13 @@
package requests
type QuestionRequest struct {
GetQuestionPage // 获取题目列表-分页
AddQuestion // 新增题目
AddQuestionTest // 新增题目
PutQuestion // 修改题目
PutQuestionTest // 修改题目
DeleteQuestion // 修改题目
GetQuestionPage // 获取题目列表-分页
AddQuestion // 新增题目
AddQuestionTest // 新增题目
PutQuestion // 修改题目
PutQuestionTest // 修改题目
DeleteQuestion // 修改题目
GetQuestionCount // 获取题目数量
}
// GetQuestionPage 获取题目列表-分页
@ -44,6 +45,7 @@ type AddQuestion struct {
SecondLabelId string `json:"second_label_id" form:"second_label_id" label:"二级标签id"`
QuestionImage []string `json:"question_image" form:"question_image" label:"图片"`
QuestionOption []string `json:"question_option" form:"question_option" label:"选项"`
QaId string `json:"qa_id" form:"qa_id" label:"题库id"`
}
// AddQuestionTest 新增题目
@ -102,3 +104,13 @@ type PutQuestionTest struct {
type DeleteQuestion struct {
QuestionId []string `json:"question_id" form:"question_id" validate:"required" label:"题目id"`
}
// GetQuestionCount 获取题目数量
type GetQuestionCount struct {
QuestionType *int `json:"question_type" form:"question_type" validate:"omitempty,oneof=1 2 3 4" label:"题目类型"` // 题目类型(1:单选 2:多选 3:问答 4:判断)
QuestionStatus *int `json:"question_status" form:"question_status" label:"状态"`
QuestionSource *int `json:"question_source" form:"question_source" validate:"omitempty,oneof=1 2" label:"题目来源"` // 题目来源1:本题库 2:外部数据)
Difficulty *int `json:"difficulty" form:"difficulty" label:"难度"`
FirstLabelId *string `json:"first_label_id" form:"first_label_id" label:"一级标签id"`
SecondLabelId *string `json:"second_label_id" form:"second_label_id" label:"二级标签id"`
}

View File

@ -6,6 +6,7 @@ type QuestionQaRequest struct {
PutQuestionQa // 修改问答题库
PutQuestionQaPassword // 修改问答题库密码
PutQuestionQaExpire // 修改问答题库有效期
PutQuestionQaRule // 修改问答题库规则解释
}
// GetQuestionQaPage 获取问答题库列表-分页
@ -19,7 +20,7 @@ type GetQuestionQaPage struct {
QaDisplayType *int `json:"qa_display_type" form:"qa_display_type" label:"展示类型"` // 1:常规 2:飞花令)
QaExpireTime string `json:"qa_expire_time" form:"qa_expire_time" label:"过期时间"` // 注意这里假设LocalTime转换为字符串格式处理
CreatedAt string `json:"created_at" form:"created_at" label:"创建时间"` // 注意这里假设LocalTime转换为字符串格式处理
UpdatedAt string `json:"updated_at" form:"updated_at" label:"过期时间"` // 注意这里假设LocalTime转换为字符串格式处理
UpdatedAt string `json:"updated_at" form:"updated_at" label:"修改时间"` // 注意这里假设LocalTime转换为字符串格式处理
Order *GetQuestionQaPageOrder `json:"order" form:"order" label:"排序"`
}
@ -80,3 +81,8 @@ type PutQuestionQaPassword struct {
type PutQuestionQaExpire struct {
QaExpireTime string `json:"qa_expire_time" form:"qa_expire_time" label:"过期时间" validate:"required"` // 注意这里假设LocalTime转换为字符串格式处理
}
// PutQuestionQaRule 修改问答题库规则解释
type PutQuestionQaRule struct {
QaRuleContent string `json:"qa_rule_content" form:"qa_rule_content" label:"规则解释" validate:"required"`
}

View File

@ -0,0 +1,38 @@
package requests
type QuestionQaItemRequest struct {
GetQuestionQaItemPage // 获取问答题库明细列表-分页
DeleteQuestionQaItem // 删除问答题库明细
PutQuestionQaItemMust // 修改必选
}
// GetQuestionQaItemPage 获取问答题库明细列表-分页
type GetQuestionQaItemPage struct {
Page int `json:"page" form:"page" label:"页码"`
PageSize int `json:"page_size" form:"page_size" label:"每页个数"`
QaId string `json:"qa_id" form:"qa_id" label:"题库id" validate:"required"`
QuestionId string `json:"question_id" form:"question_id" label:"题目id"`
IsMustSelect *int `json:"is_must_select" form:"is_must_select" label:"是否必被选中"`
QuestionName string `json:"question_name" form:"question_name" label:"题目名称"`
QuestionType *int `json:"question_type" form:"question_type" validate:"omitempty,oneof=1 2 3 4" label:"题目类型"` // 题目类型(1:单选 2:多选 3:问答 4:判断)
QuestionSource *int `json:"question_source" form:"question_source" validate:"omitempty,oneof=1 2" label:"题目来源"` // 题目来源1:本题库 2:外部数据)
Difficulty *int `json:"difficulty" form:"difficulty" label:"难度"`
FirstLabelId *string `json:"first_label_id" form:"first_label_id" label:"一级标签id"`
SecondLabelId *string `json:"second_label_id" form:"second_label_id" label:"二级标签id"`
Order *GetQuestionQaItemPageOrder `json:"order" form:"order" label:"排序"`
}
// GetQuestionQaItemPageOrder 获取题目明细列表-分页-排序条件
type GetQuestionQaItemPageOrder struct {
UpdatedAt string `json:"updated_at" form:"updated_at" label:"排序"`
}
// DeleteQuestionQaItem 删除问答题库明细
type DeleteQuestionQaItem struct {
ItemId []string `json:"item_id" form:"item_id" validate:"required" label:"明细id"`
}
// PutQuestionQaItemMust 修改必选
type PutQuestionQaItemMust struct {
IsMustSelect int `json:"is_must_select" form:"is_must_select" label:"是否必被选中"`
}

10
api/requests/Share.go Normal file
View File

@ -0,0 +1,10 @@
package requests
type ShareRequest struct {
GetShare // 获取分享数据
}
type GetShare struct {
QaShareId string `json:"qa_share_id" form:"qa_share_id" label:"分享标识" validate:"required"`
QaPassword string `json:"qa_password" form:"qa_password" label:"密码" validate:"required"`
}

View File

@ -90,6 +90,13 @@ func publicRouter(r *gin.Engine, api controller.Api) {
// 修改题目
adminGroup.PUT("/question/test/:question_id", api.Question.PutQuestionTest)
// 分享
shareGroup := r.Group("/share")
{
// 获取分享数据
shareGroup.GET("", api.Share.GetShare)
}
}
// adminRouter 公共路由-验证权限
@ -112,6 +119,9 @@ func privateRouter(r *gin.Engine, api controller.Api) {
// 获取题目列表-分页
questionGroup.POST("/page", api.Question.GetQuestionPage)
// 获取题目详情
questionGroup.GET("/:question_id", api.Question.GetQuestion)
// 新增题目
questionGroup.POST("", api.Question.AddQuestion)
@ -120,6 +130,9 @@ func privateRouter(r *gin.Engine, api controller.Api) {
// 删除题目
questionGroup.DELETE("", api.Question.DeleteQuestion)
// 获取题目数量
questionGroup.GET("/count", api.Question.GetQuestionCount)
}
// 问答
@ -132,13 +145,52 @@ func privateRouter(r *gin.Engine, api controller.Api) {
qaGroup.POST("", api.QuestionQa.AddQuestionQa)
// 修改问答题库
qaGroup.PUT("/:qa_id", api.Question.PutQuestionQa)
qaGroup.PUT("/:qa_id", api.QuestionQa.PutQuestionQa)
// 修改问答题库密码
qaGroup.PUT("/password/:qa_id", api.Question.PutQuestionQaPassword)
qaGroup.PUT("/password/:qa_id", api.QuestionQa.PutQuestionQaPassword)
// 修改问答题库有效期
qaGroup.PUT("/expire/:qa_id", api.Question.PutQuestionQaExpire)
qaGroup.PUT("/expire/:qa_id", api.QuestionQa.PutQuestionQaExpire)
// 修改问答题库规则解释
qaGroup.PUT("/rule/:qa_id", api.QuestionQa.PutQuestionQaRule)
// 获取问答题库详情
qaGroup.GET("/:qa_id", api.QuestionQa.GetQuestionQa)
// 题库明细
itemGroup := qaGroup.Group("/item")
{
// 获取问答题库明细列表-分页
itemGroup.POST("/page", api.QuestionQaItem.GetQuestionQaItemPage)
// 删除问答题库明细
itemGroup.DELETE("", api.QuestionQaItem.DeleteQuestionQaItem)
// 修改必选
itemGroup.PUT("/must/:item_id", api.QuestionQaItem.PutQuestionQaItemMust)
}
}
// 标签
labelGroup := adminGroup.Group("/label")
{
// 获取标签列表
labelGroup.GET("/list", api.Label.GetLabelList)
// 新增标签
labelGroup.POST("", api.Label.AddLabel)
// 修改标签
labelGroup.PUT("/:label_id", api.Label.PutLabel)
}
// 统计
staticGroup := adminGroup.Group("/static")
{
// 获取统计数据
staticGroup.GET("", api.Static.GetStatic)
}
}

88
api/service/Label.go Normal file
View File

@ -0,0 +1,88 @@
package service
import (
"errors"
"knowledge/api/dao"
"knowledge/api/model"
"knowledge/api/requests"
"knowledge/global"
"strconv"
)
type LabelService struct {
}
// AddLabel 新增标签
func (r *LabelService) AddLabel(req requests.AddLabel) (bool, error) {
labelDao := dao.LabelDao{}
var parentId int64
if req.LabelLevel != 1 {
if req.ParentId == "" || req.ParentId == "0" {
return false, errors.New("请填写父级标签")
}
parentId, err := strconv.ParseInt(req.ParentId, 10, 64)
if err != nil {
return false, err
}
_, err = labelDao.GetLabelFirstById(parentId)
if err != nil {
return false, errors.New("父级标签错误")
}
}
// 开始事务
tx := global.Db.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
// 新增题目
label := &model.Label{
LabelName: req.LabelName,
ParentId: parentId,
LabelLevel: req.LabelLevel,
}
label, err := labelDao.AddLabel(tx, label)
if err != nil {
tx.Rollback()
return false, errors.New("新增失败")
}
tx.Commit()
return true, nil
}
// PutLabel 修改标签
func (r *LabelService) PutLabel(labelId int64, req requests.PutLabel) (bool, error) {
labelDao := dao.LabelDao{}
_, err := labelDao.GetLabelFirstById(labelId)
if err != nil {
return false, errors.New("标签不存在")
}
// 开始事务
tx := global.Db.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
labelData := make(map[string]interface{})
labelData["label_name"] = req.LabelName
err = labelDao.EditLabelById(tx, labelId, labelData)
if err != nil {
tx.Rollback()
return false, errors.New(err.Error())
}
tx.Commit()
return true, nil
}

View File

@ -5,6 +5,7 @@ import (
"fmt"
"gorm.io/gorm"
"knowledge/api/dao"
"knowledge/api/dto"
"knowledge/api/model"
"knowledge/api/requests"
"knowledge/global"
@ -119,6 +120,29 @@ func (r *QuestionService) AddQuestion(req requests.AddQuestion) (bool, error) {
}
}
// 处理题库数据
if req.QaId != "" {
qaId, err := strconv.ParseInt(req.QaId, 10, 64)
if err != nil {
tx.Rollback()
return false, err
}
// 新增题库明细
questionQaItemDao := dao.QuestionQaItemDao{}
questionQaItem := &model.QuestionQaItem{
QaId: qaId,
QuestionId: question.QuestionId,
IsMustSelect: 0,
}
_, err = questionQaItemDao.AddQuestionQaItem(tx, questionQaItem)
if err != nil {
tx.Rollback()
return false, err
}
}
tx.Commit()
return true, nil
}
@ -746,3 +770,34 @@ func (r *QuestionService) DeleteQuestion(req requests.DeleteQuestion) (bool, err
return true, nil
}
// GetQuestion 获取题目详情
func (r *QuestionService) GetQuestion(questionId int64) (g *dto.QuestionDto, err error) {
questionDao := dao.QuestionDao{}
question, err := questionDao.GetQuestionPreloadById(questionId)
if err != nil {
return nil, errors.New("题目不存在")
}
if question.QuestionStatus == 2 {
return nil, errors.New("题目已禁用")
}
if question.IsDelete == 1 {
return nil, errors.New("题目已删除")
}
// 处理返回值
g = dto.GetQuestionDto(question)
// 加载一级标签
g.LoadFirstLabel(question.FirstLabel)
// 加载二级标签
g.LoadSecondLabel(question.SecondLabel)
// 加载选项
g.LoadQuestionOption(question.QuestionOption)
return g, nil
}

View File

@ -2,7 +2,10 @@ package service
import (
"errors"
"fmt"
"github.com/goccy/go-json"
"knowledge/api/dao"
"knowledge/api/dto"
"knowledge/api/model"
"knowledge/api/requests"
"knowledge/global"
@ -39,8 +42,8 @@ func (r *QuestionQaService) AddQuestionQa(req requests.AddQuestionQa) (bool, err
return false, errors.New("过期时间需大于当前时间")
}
// 生成分享链接
qaLink := ""
// 处理明细选题规则
itemContent, _ := json.Marshal(req.Item)
// 开始事务
tx := global.Db.Begin()
@ -58,10 +61,10 @@ func (r *QuestionQaService) AddQuestionQa(req requests.AddQuestionQa) (bool, err
QaRuleContent: req.QaRuleContent,
QaDisplayType: req.QaDisplayType,
QaExpireTime: model.LocalTime(qaExpireTime),
QaLink: qaLink,
QaPassword: req.QaPassword,
OpenNumber: 0,
Image: req.Image,
ItemContent: string(itemContent),
}
questionQaDao := dao.QuestionQaDao{}
@ -71,6 +74,21 @@ func (r *QuestionQaService) AddQuestionQa(req requests.AddQuestionQa) (bool, err
return false, errors.New("新增失败")
}
// 生成分享链接
qaShareId := utils.HashString(fmt.Sprintf("%d", questionQa.QaId))
if err != nil {
tx.Rollback()
return false, err
}
questionQaData := make(map[string]interface{})
questionQaData["qa_share_id"] = qaShareId
err = questionQaDao.EditQuestionQaById(tx, questionQa.QaId, questionQaData)
if err != nil {
tx.Rollback()
return false, errors.New("新增失败")
}
// 新增问答题库题目列表
questionDao := dao.QuestionDao{}
questionQaItemDao := dao.QuestionQaItemDao{}
@ -129,7 +147,7 @@ func (r *QuestionQaService) AddQuestionQa(req requests.AddQuestionQa) (bool, err
}
// 获取随机明细题目
questions, err := questionDao.GetQuestionListRand(maps, req.QaQuantity)
questions, err := questionDao.GetQuestionListRand(maps, item.Quantity*3)
if err != nil {
tx.Rollback()
return false, err
@ -223,6 +241,12 @@ func (r *QuestionQaService) PutQuestionQa(qaId int64, req requests.PutQuestionQa
questionQaData["image"] = image
}
// 处理明细选题规则
itemContent, _ := json.Marshal(req.Item)
if string(itemContent) != questionQa.ItemContent {
questionQaData["item_content"] = string(itemContent)
}
// 题目数量
if req.QaQuantity != questionQa.QaQuantity {
if req.Action == 1 {
@ -298,7 +322,7 @@ func (r *QuestionQaService) PutQuestionQa(qaId int64, req requests.PutQuestionQa
}
// 获取随机明细题目
questions, err := questionDao.GetQuestionListRand(maps, req.QaQuantity)
questions, err := questionDao.GetQuestionListRand(maps, req.QaQuantity*3)
if err != nil {
tx.Rollback()
return false, err
@ -418,3 +442,62 @@ func (r *QuestionQaService) PutQuestionQaExpire(qaId int64, req requests.PutQues
tx.Commit()
return true, nil
}
// PutQuestionQaRule 修改问答题库规则解释
func (r *QuestionQaService) PutQuestionQaRule(qaId int64, req requests.PutQuestionQaRule) (bool, error) {
questionQaDao := dao.QuestionQaDao{}
questionQa, err := questionQaDao.GetQuestionQaById(qaId)
if err != nil {
return false, errors.New("题库不存在")
}
if questionQa.QaStatus == 2 {
return false, errors.New("题库已失效")
}
// 分享密码
if req.QaRuleContent == questionQa.QaRuleContent {
return true, nil
}
// 开始事务
tx := global.Db.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
questionQaData := make(map[string]interface{})
questionQaData["qa_rule_content"] = req.QaRuleContent
err = questionQaDao.EditQuestionQaById(tx, questionQa.QaId, questionQaData)
if err != nil {
tx.Rollback()
return false, errors.New(err.Error())
}
tx.Commit()
return true, nil
}
// GetQuestionQa 获取问答题库详情
func (r *QuestionQaService) GetQuestionQa(qaId int64) (g *dto.QuestionQaDto, err error) {
questionQaDao := dao.QuestionQaDao{}
questionQa, err := questionQaDao.GetQuestionQaById(qaId)
if err != nil {
return nil, errors.New("题库不存在")
}
if questionQa.QaStatus == 2 {
return nil, errors.New("题库已失效")
}
// 处理返回值
g = dto.GetQuestionQaDto(questionQa)
// 加载明细选题规则
g = g.LoadItemContent(questionQa.ItemContent)
return g, nil
}

View File

@ -0,0 +1,84 @@
package service
import (
"errors"
"knowledge/api/dao"
"knowledge/api/requests"
"knowledge/global"
"strconv"
)
type QuestionQaItemService struct {
}
// DeleteQuestionQaItem 删除问答题库明细
func (r *QuestionQaItemService) DeleteQuestionQaItem(req requests.DeleteQuestionQaItem) (bool, error) {
questionQaItemDao := dao.QuestionQaItemDao{}
// 开始事务
tx := global.Db.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
for _, itemId := range req.ItemId {
// 将字符串转换为int64类型
itemId, err := strconv.ParseInt(itemId, 10, 64)
if err != nil {
tx.Rollback()
return false, errors.New("删除失败")
}
_, err = questionQaItemDao.GetQuestionQaItemById(itemId)
if err != nil {
tx.Rollback()
return false, errors.New("明细不存在")
}
err = questionQaItemDao.DeleteQuestionQaItemById(tx, itemId)
if err != nil {
tx.Rollback()
return false, errors.New("删除失败")
}
}
tx.Commit()
return true, nil
}
// PutQuestionQaItemMust 修改必选
func (r *QuestionQaItemService) PutQuestionQaItemMust(itemId int64, req requests.PutQuestionQaItemMust) (bool, error) {
questionQaItemDao := dao.QuestionQaItemDao{}
questionQaItem, err := questionQaItemDao.GetQuestionQaItemById(itemId)
if err != nil {
return false, errors.New("题目不存在")
}
// 分享密码
if req.IsMustSelect == questionQaItem.IsMustSelect {
return true, nil
}
// 开始事务
tx := global.Db.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
questionQaItemData := make(map[string]interface{})
questionQaItemData["is_must_select"] = req.IsMustSelect
err = questionQaItemDao.EditQuestionQaItemById(tx, itemId, questionQaItemData)
if err != nil {
tx.Rollback()
return false, errors.New(err.Error())
}
tx.Commit()
return true, nil
}

106
api/service/Share.go Normal file
View File

@ -0,0 +1,106 @@
package service
import (
"errors"
"knowledge/api/dao"
"knowledge/api/dto"
"knowledge/api/requests"
"sort"
"time"
)
type ShareService struct {
}
func (r *ShareService) GetShare(req requests.GetShare) (g *dto.ShareDto, err error) {
questionQaDao := dao.QuestionQaDao{}
questionQaItemDao := dao.QuestionQaItemDao{}
questionDao := dao.QuestionDao{}
// 获取题库数据
maps := make(map[string]interface{})
maps["qa_share_id"] = req.QaShareId
questionQa, err := questionQaDao.GetQuestionQa(maps)
if err != nil || questionQa == nil {
return nil, errors.New("题库不存在")
}
if questionQa.QaStatus == 2 {
return nil, errors.New("题库已失效")
}
if questionQa.QaPassword != req.QaPassword {
return nil, errors.New("密码错误")
}
// 检测过期时间
now := time.Now()
qaExpireTime := time.Time(questionQa.QaExpireTime)
if qaExpireTime.Before(now) {
return nil, errors.New("题库已失效")
}
// 加载题库数据
g = &dto.ShareDto{}
g.QuestionQa = dto.GetQuestionQaDto(questionQa)
// 获取题库明细数据-必备选中
maps = make(map[string]interface{})
maps["qa_id"] = questionQa.QaId
maps["is_must_select"] = 1
questionQaItems, err := questionQaItemDao.GetQuestionQaItemList(maps)
if err == nil && len(questionQaItems) > 0 {
shareQuestionDtos := make([]*dto.ShareQuestionDto, len(questionQaItems))
for i, item := range questionQaItems {
// 获取题目数据
question, err := questionDao.GetQuestionById(item.QuestionId)
if err != nil {
return nil, errors.New("题目错误")
}
shareQuestionDto := dto.GetShareQuestionDto(question)
shareQuestionDto.IsMustSelect = item.IsMustSelect
// 将转换后的结构体添加到新切片中
shareQuestionDtos[i] = shareQuestionDto
}
g.Question = shareQuestionDtos
}
// 计算剩余题目数量
remainingQuantity := questionQa.QaQuantity - len(questionQaItems)
if remainingQuantity > 0 {
// 随机获取剩余题目
maps = make(map[string]interface{})
maps["qa_id"] = questionQa.QaId
maps["is_must_select"] = 0
questionQaItems, err = questionQaItemDao.GetQuestionQaItemListRand(maps, 1)
if err == nil && len(questionQaItems) > 0 {
for _, item := range questionQaItems {
// 获取题目数据
question, err := questionDao.GetQuestionById(item.QuestionId)
if err != nil {
return nil, errors.New("题目错误")
}
shareQuestionDto := dto.GetShareQuestionDto(question)
shareQuestionDto.IsMustSelect = item.IsMustSelect
// 将转换后的结构体添加到新切片中
g.Question = append(g.Question, shareQuestionDto)
}
}
}
// 按照难度重新排序题目顺序
sort.SliceStable(g.Question, func(i, j int) bool {
return g.Question[i].Difficulty < g.Question[j].Difficulty
})
return g, nil
}

93
api/service/Static.go Normal file
View File

@ -0,0 +1,93 @@
package service
import (
"knowledge/api/dao"
"knowledge/api/dto"
)
type StaticService struct {
}
func (r *StaticService) GetStatic() (g []*dto.StaticDto, err error) {
questionDao := dao.QuestionDao{}
// 处理返回值
var responses []*dto.StaticDto
// 获取每个题目类型题目数量
maps := make(map[string]interface{})
maps["question_type"] = 1
maps["question_status"] = 1
maps["question_source"] = 1
total, err := questionDao.GetQuestionCount(maps)
response := &dto.StaticDto{
Name: "单选题",
Total: int(total),
}
responses = append(responses, response)
// 获取每个题目类型题目数量
maps = make(map[string]interface{})
maps["question_type"] = 2
maps["question_status"] = 1
maps["question_source"] = 1
total, err = questionDao.GetQuestionCount(maps)
response = &dto.StaticDto{
Name: "多选题",
Total: int(total),
}
responses = append(responses, response)
// 获取每个题目类型题目数量
maps = make(map[string]interface{})
maps["question_type"] = 3
maps["question_status"] = 1
maps["question_source"] = 1
total, err = questionDao.GetQuestionCount(maps)
response = &dto.StaticDto{
Name: "问答题",
Total: int(total),
}
responses = append(responses, response)
// 获取每个题目类型题目数量
maps = make(map[string]interface{})
maps["question_type"] = 4
maps["question_status"] = 1
maps["question_source"] = 1
total, err = questionDao.GetQuestionCount(maps)
response = &dto.StaticDto{
Name: "判断题",
Total: int(total),
}
responses = append(responses, response)
// 获取每个一级标签题目数量
labelDao := dao.LabelDao{}
maps = make(map[string]interface{})
maps["parent_id"] = 0
maps["label_level"] = 1
labels, err := labelDao.GetLabelList(maps)
if err != nil {
return nil, err
}
for _, label := range labels {
maps = make(map[string]interface{})
maps["question_status"] = 1
maps["question_source"] = 1
maps["first_label_id"] = label.LabelId
total, err = questionDao.GetQuestionCount(maps)
response = &dto.StaticDto{
Name: label.LabelName,
Total: int(total),
}
responses = append(responses, response)
}
return responses, nil
}

View File

@ -4,6 +4,8 @@ env: dev # 环境配置
snowflake: 1 # 雪花算法机器id
domain: http://dev-knowledge.com
# [数据库配置]
mysql:
host: '42.193.16.243'

View File

@ -5,6 +5,7 @@ var C Config
type Config struct {
Port int `mapstructure:"port" json:"port" yaml:"port"`
Env string `mapstructure:"env" json:"Env" yaml:"Env"`
Domain string `mapstructure:"domain" json:"domain" yaml:"domain"`
Mysql Mysql `mapstructure:"mysql" json:"mysql" yaml:"mysql"`
Log Log `mapstructure:"log" json:"log" yaml:"log"`
Redis Redis `mapstructure:"redis" json:"redis" yaml:"redis"`

90
utils/aes.go Normal file
View File

@ -0,0 +1,90 @@
package utils
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"io"
)
// Encrypt 加密函数
func Encrypt(text, key string) (string, error) {
plaintext := []byte(text)
hashedKey := hashKey(key)
// 创建一个新的 AES cipher.Block
block, err := aes.NewCipher(hashedKey)
if err != nil {
return "", err
}
// 使用 GCM 模式加密
aesGCM, err := cipher.NewGCM(block)
if err != nil {
return "", err
}
nonce := make([]byte, aesGCM.NonceSize())
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
return "", err
}
// 加密数据
ciphertext := aesGCM.Seal(nonce, nonce, plaintext, nil)
return base64.StdEncoding.EncodeToString(ciphertext), nil
}
// Decrypt 解密函数
func Decrypt(cryptoText, key string) (string, error) {
ciphertext, _ := base64.StdEncoding.DecodeString(cryptoText)
hashedKey := hashKey(key)
// 创建一个新的 AES cipher.Block
block, err := aes.NewCipher(hashedKey)
if err != nil {
return "", err
}
// 使用 GCM 模式解密
aesGCM, err := cipher.NewGCM(block)
if err != nil {
return "", err
}
nonceSize := aesGCM.NonceSize()
nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
// 解密数据
plaintext, err := aesGCM.Open(nil, nonce, ciphertext, nil)
if err != nil {
return "", err
}
return string(plaintext), nil
}
// HashString 使用SHA256算法对字符串进行哈希加密
func HashString(s string) string {
// 创建一个新的SHA256哈希实例
hasher := sha256.New()
// 写入要哈希的字符串的字节
hasher.Write([]byte(s))
// 获取哈希结果的字节切片
hashBytes := hasher.Sum(nil)
// 将字节切片转换成十六进制字符串表示
s = hex.EncodeToString(hashBytes)
return s
}
// 将任意长度的密钥转换为适合 AES 的长度32 字节)
func hashKey(key string) []byte {
hash := sha256.Sum256([]byte(key))
return hash[:]
}

View File

@ -20,3 +20,11 @@ func AddOssDomain(url string) string {
}
return config.C.Oss.OssCustomDomainName + url
}
// AddDomain 增加地址中的前缀
func AddDomain(url string) string {
if url == "" {
return ""
}
return config.C.Domain + "/share/" + url
}