新增了 问答题库管理

This commit is contained in:
wucongxing8150 2024-06-28 17:46:31 +08:00
parent 989ae25809
commit c87c7076df
20 changed files with 1313 additions and 11 deletions

View File

@ -3,7 +3,8 @@ package controller
// Api api接口
type Api struct {
Migrate
Public // 公共方法-不验证权限
AdminUser // 后台用户
Question // 题目
Public // 公共方法-不验证权限
AdminUser // 后台用户
Question // 题目
QuestionQa // 问答题库
}

View File

@ -188,3 +188,29 @@ func (r *Question) PutQuestionTest(c *gin.Context) {
responses.Ok(c)
}
// DeleteQuestion 删除题目
func (r *Question) DeleteQuestion(c *gin.Context) {
couponRequest := requests.QuestionRequest{}
req := couponRequest.DeleteQuestion
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
}
// 业务处理
questionService := service.QuestionService{}
_, err := questionService.DeleteQuestion(req)
if err != nil {
responses.FailWithMessage(err.Error(), c)
return
}
responses.Ok(c)
}

View File

@ -0,0 +1,219 @@
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 QuestionQa struct{}
// GetQuestionQaPage 获取问答题库列表-分页
func (r *QuestionQa) GetQuestionQaPage(c *gin.Context) {
questionQaRequest := requests.QuestionQaRequest{}
req := questionQaRequest.GetQuestionQaPage
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
}
questionQaDao := dao.QuestionQaDao{}
questionQa, total, err := questionQaDao.GetQuestionQaPageSearch(req, req.Page, req.PageSize)
if err != nil {
responses.FailWithMessage(err.Error(), c)
return
}
// 处理返回值
GetQuestionQaPageResponses := dto.GetQuestionQaListDto(questionQa)
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"] = GetQuestionQaPageResponses
responses.OkWithData(result, c)
}
// AddQuestionQa 新增问答题库
func (r *QuestionQa) AddQuestionQa(c *gin.Context) {
questionQaRequest := requests.QuestionQaRequest{}
req := questionQaRequest.AddQuestionQa
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
}
// 参数验证
for _, item := range req.Item {
if err := global.Validate.Struct(item); err != nil {
responses.FailWithMessage(utils.Translate(err), c)
return
}
}
// 业务处理
questionQaService := service.QuestionQaService{}
_, err := questionQaService.AddQuestionQa(req)
if err != nil {
responses.FailWithMessage(err.Error(), c)
return
}
responses.Ok(c)
}
// PutQuestionQa 修改问答题库
func (r *Question) PutQuestionQa(c *gin.Context) {
questionQaRequest := requests.QuestionQaRequest{}
req := questionQaRequest.PutQuestionQa
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
}
// 参数验证
for _, item := range req.Item {
if err := global.Validate.Struct(item); 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.PutQuestionQa(qaId, req)
if err != nil {
responses.FailWithMessage(err.Error(), c)
return
}
responses.Ok(c)
}
// PutQuestionQaPassword 修改问答题库密码
func (r *Question) PutQuestionQaPassword(c *gin.Context) {
questionQaRequest := requests.QuestionQaRequest{}
req := questionQaRequest.PutQuestionQaPassword
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.PutQuestionQaPassword(qaId, req)
if err != nil {
responses.FailWithMessage(err.Error(), c)
return
}
responses.Ok(c)
}
// PutQuestionQaExpire 修改问答题库有效期
func (r *Question) PutQuestionQaExpire(c *gin.Context) {
questionQaRequest := requests.QuestionQaRequest{}
req := questionQaRequest.PutQuestionQaExpire
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.PutQuestionQaExpire(qaId, req)
if err != nil {
responses.FailWithMessage(err.Error(), c)
return
}
responses.Ok(c)
}

View File

@ -0,0 +1,46 @@
package crontab
import (
"knowledge/api/dao"
"knowledge/global"
"knowledge/utils"
"time"
)
// HandleQuestionQaExpire 题目题库过期处理
func HandleQuestionQaExpire() {
// 获取过期题目
questionQaDao := dao.QuestionQaDao{}
// 验证数量
maps := make(map[string]interface{})
maps["qa_status"] = 1
now := time.Now()
// 当前时间
endTime := now.Format("2006-01-02 15:04")
// 今天开始时间
year, month, day := now.Date()
location := now.Location()
startTime := time.Date(year, month, day, 00, 00, 00, 0, location).Format("2006-01-02 15:04")
questionQas, err := questionQaDao.GetQuestionQaListByQaExpireTime(maps, startTime, endTime)
if err == nil && len(questionQas) > 0 {
for _, qa := range questionQas {
// 检测状态
if qa.QaStatus == 2 {
continue
}
// 修改为失效
questionQaData := make(map[string]interface{})
questionQaData["qa_status"] = 2
err := questionQaDao.EditQuestionQaById(global.Db, qa.QaId, questionQaData)
if err != nil {
utils.LogJsonErr("处理题目题库过期失败:", err)
}
}
}
}

View File

@ -1,6 +1,7 @@
package dao
import (
"errors"
"gorm.io/gorm"
"knowledge/api/model"
"knowledge/api/requests"
@ -65,6 +66,24 @@ func (r *QuestionDao) GetQuestionList(maps interface{}) (m []*model.Question, er
return m, nil
}
// GetQuestionCount 获取数量
func (r *QuestionDao) GetQuestionCount(maps interface{}) (total int64, err error) {
err = global.Db.Model(&model.Question{}).Where(maps).Count(&total).Error
if err != nil {
return 0, err
}
return total, nil
}
// 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
if err != nil {
return nil, err
}
return m, nil
}
// AddQuestion 新增
func (r *QuestionDao) AddQuestion(tx *gorm.DB, model *model.Question) (*model.Question, error) {
if err := tx.Create(model).Error; err != nil {
@ -164,6 +183,10 @@ func (r *QuestionDao) GetQuestionPageSearch(req requests.GetQuestionPage, page,
// 排序
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)
}
}

190
api/dao/QuestionQa.go Normal file
View File

@ -0,0 +1,190 @@
package dao
import (
"errors"
"gorm.io/gorm"
"knowledge/api/model"
"knowledge/api/requests"
"knowledge/global"
"strings"
"time"
)
type QuestionQaDao struct {
}
// GetQuestionQaById 获取数据-id
func (r *QuestionQaDao) GetQuestionQaById(qaId int64) (m *model.QuestionQa, err error) {
err = global.Db.First(&m, qaId).Error
if err != nil {
return nil, err
}
return m, nil
}
// DeleteQuestionQa 删除
func (r *QuestionQaDao) DeleteQuestionQa(tx *gorm.DB, maps interface{}) error {
err := tx.Where(maps).Delete(&model.QuestionQa{}).Error
if err != nil {
return err
}
return nil
}
// DeleteQuestionQaById 删除-id
func (r *QuestionQaDao) DeleteQuestionQaById(tx *gorm.DB, qaId int64) error {
if err := tx.Delete(&model.QuestionQa{}, qaId).Error; err != nil {
return err
}
return nil
}
// EditQuestionQa 修改
func (r *QuestionQaDao) EditQuestionQa(tx *gorm.DB, maps interface{}, data interface{}) error {
err := tx.Model(&model.QuestionQa{}).Where(maps).Updates(data).Error
if err != nil {
return err
}
return nil
}
// EditQuestionQaById 修改-id
func (r *QuestionQaDao) EditQuestionQaById(tx *gorm.DB, qaId int64, data interface{}) error {
err := tx.Model(&model.QuestionQa{}).Where("qa_id = ?", qaId).Updates(data).Error
if err != nil {
return err
}
return nil
}
// GetQuestionQaList 获取列表
func (r *QuestionQaDao) GetQuestionQaList(maps interface{}) (m []*model.QuestionQa, err error) {
err = global.Db.Where(maps).Find(&m).Error
if err != nil {
return nil, err
}
return m, nil
}
// AddQuestionQa 新增
func (r *QuestionQaDao) AddQuestionQa(tx *gorm.DB, model *model.QuestionQa) (*model.QuestionQa, error) {
if err := tx.Create(model).Error; err != nil {
return nil, err
}
return model, nil
}
// GetQuestionQa 获取
func (r *QuestionQaDao) GetQuestionQa(maps interface{}) (m *model.QuestionQa, err error) {
err = global.Db.Where(maps).First(&m).Error
if err != nil {
return nil, err
}
return m, nil
}
// GetQuestionQaListByQaExpireTime 获取列表-今天开始时间/过期时间
func (r *QuestionQaDao) GetQuestionQaListByQaExpireTime(maps interface{}, startTime, endTime string) (m []*model.QuestionQa, err error) {
err = global.Db.Where(maps).Where("qa_expire_time BETWEEN ? AND ?", startTime, endTime).Find(&m).Error
if err != nil {
return nil, err
}
return m, nil
}
// GetQuestionQaPageSearch 获取题目列表-分页
func (r *QuestionQaDao) GetQuestionQaPageSearch(req requests.GetQuestionQaPage, page, pageSize int) (m []*model.QuestionQa, total int64, err error) {
var totalRecords int64
// 构建查询条件
query := global.Db.Model(&model.QuestionQa{})
// 主键id
if req.QaId != "" {
query = query.Where("qa_id = ?", req.QaId)
}
// 名称
if req.QaName != "" {
query = query.Where("qa_name LIKE ?", "%"+req.QaName+"%")
}
// 状态
if req.QaStatus != nil {
query = query.Where("qa_status = ?", req.QaStatus)
}
// 规则解释
if req.QaRuleContent != "" {
query = query.Where("qa_rule_content LIKE ?", "%"+req.QaRuleContent+"%")
}
// 展示类型
if req.QaDisplayType != nil {
query = query.Where("qa_display_type = ?", req.QaDisplayType)
}
// 过期时间
if req.QaExpireTime != "" {
qaExpireTime := strings.Split(req.QaExpireTime, "&")
if len(qaExpireTime) == 2 {
startTime, _ := time.Parse("2006-01-02", qaExpireTime[0])
endTime, _ := time.Parse("2006-01-02", qaExpireTime[1])
endTime = endTime.Add(23*time.Hour + 59*time.Minute + 59*time.Second)
query = query.Where("qa_expire_time BETWEEN ? AND ?", startTime, endTime)
}
}
// 创建时间
if req.CreatedAt != "" {
createdAt := strings.Split(req.CreatedAt, "&")
if len(createdAt) == 2 {
startTime, _ := time.Parse("2006-01-02", createdAt[0])
endTime, _ := time.Parse("2006-01-02", createdAt[1])
endTime = endTime.Add(23*time.Hour + 59*time.Minute + 59*time.Second)
query = query.Where("created_at BETWEEN ? AND ?", startTime, endTime)
}
}
// 修改时间
if req.UpdatedAt != "" {
updatedAt := strings.Split(req.UpdatedAt, "&")
if len(updatedAt) == 2 {
startTime, _ := time.Parse("2006-01-02", updatedAt[0])
endTime, _ := time.Parse("2006-01-02", updatedAt[1])
endTime = endTime.Add(23*time.Hour + 59*time.Minute + 59*time.Second)
query = query.Where("updated_at BETWEEN ? AND ?", startTime, endTime)
}
}
// 排序
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
}

71
api/dao/QuestionQaItem.go Normal file
View File

@ -0,0 +1,71 @@
package dao
import (
"gorm.io/gorm"
"knowledge/api/model"
"knowledge/global"
)
type QuestionQaItemDao struct {
}
// GetQuestionQaItemById 获取数据-id
func (r *QuestionQaItemDao) GetQuestionQaItemById(qaId int64) (m *model.QuestionQaItem, err error) {
err = global.Db.First(&m, qaId).Error
if err != nil {
return nil, err
}
return m, nil
}
// DeleteQuestionQaItem 删除
func (r *QuestionQaItemDao) DeleteQuestionQaItem(tx *gorm.DB, maps interface{}) error {
err := tx.Where(maps).Delete(&model.QuestionQaItem{}).Error
if 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 {
return err
}
return nil
}
// EditQuestionQaItem 修改
func (r *QuestionQaItemDao) EditQuestionQaItem(tx *gorm.DB, maps interface{}, data interface{}) error {
err := tx.Model(&model.QuestionQaItem{}).Where(maps).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
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 {
return nil, err
}
return model, nil
}
// GetQuestionQaItem 获取
func (r *QuestionQaItemDao) GetQuestionQaItem(maps interface{}) (m *model.QuestionQaItem, err error) {
err = global.Db.Where(maps).First(&m).Error
if err != nil {
return nil, err
}
return m, nil
}

View File

@ -72,7 +72,6 @@ func GetQuestionListDto(m []*model.Question) []*QuestionDto {
response = response.LoadFirstLabel(v.FirstLabel)
}
fmt.Println(v.SecondLabel)
// 加载二级标签
if v.SecondLabel != nil {
response = response.LoadSecondLabel(v.SecondLabel)

52
api/dto/QuestionQa.go Normal file
View File

@ -0,0 +1,52 @@
package dto
import (
"fmt"
"knowledge/api/model"
)
// 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"` // 更新时间
}
// GetQuestionQaListDto 问答题库列表
func GetQuestionQaListDto(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,
QaLink: v.QaLink,
OpenNumber: v.OpenNumber,
CreatedAt: v.CreatedAt,
UpdatedAt: v.UpdatedAt,
}
// 将转换后的结构体添加到新切片中
responses[i] = response
}
}
return responses
}

41
api/model/QuestionQa.go Normal file
View File

@ -0,0 +1,41 @@
package model
import (
"gorm.io/gorm"
"knowledge/global"
"time"
)
// QuestionQa 知识问答
type QuestionQa struct {
QaId int64 `gorm:"column:qa_id;type:bigint(19);primary_key;comment:主键id" json:"qa_id"`
QaName string `gorm:"column:qa_name;type:varchar(255);comment:名称" json:"qa_name"`
QaQuantity int `gorm:"column:qa_quantity;type:int(5);default:0;comment:题目数量" json:"qa_quantity"`
QaStatus int `gorm:"column:qa_status;type:int(1);default:1;comment:状态1:正常 2:过期)" json:"qa_status"`
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"`
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"`
Model
}
func (m *QuestionQa) TableName() string {
return "kb_question_qa"
}
func (m *QuestionQa) BeforeCreate(tx *gorm.DB) error {
if m.QaId == 0 {
m.QaId = global.Snowflake.Generate().Int64()
}
m.CreatedAt = LocalTime(time.Now())
tx.Statement.SetColumn("CreatedAt", m.CreatedAt)
m.UpdatedAt = LocalTime(time.Now())
tx.Statement.SetColumn("UpdatedAt", m.UpdatedAt)
return nil
}

View File

@ -0,0 +1,34 @@
package model
import (
"gorm.io/gorm"
"knowledge/global"
"time"
)
// QuestionQaItem 知识问答-明细
type QuestionQaItem struct {
ItemId int64 `gorm:"column:item_id;type:bigint(19);primary_key;comment:主键id" json:"item_id"`
QaId int64 `gorm:"column:qa_id;type:bigint(19);comment:知识问答id;NOT NULL" json:"qa_id"`
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
}
func (m *QuestionQaItem) TableName() string {
return "kb_question_qa_item"
}
func (m *QuestionQaItem) BeforeCreate(tx *gorm.DB) error {
if m.ItemId == 0 {
m.ItemId = global.Snowflake.Generate().Int64()
}
m.CreatedAt = LocalTime(time.Now())
tx.Statement.SetColumn("CreatedAt", m.CreatedAt)
m.UpdatedAt = LocalTime(time.Now())
tx.Statement.SetColumn("UpdatedAt", m.UpdatedAt)
return nil
}

View File

@ -6,6 +6,7 @@ type QuestionRequest struct {
AddQuestionTest // 新增题目
PutQuestion // 修改题目
PutQuestionTest // 修改题目
DeleteQuestion // 修改题目
}
// GetQuestionPage 获取题目列表-分页
@ -25,9 +26,9 @@ type GetQuestionPage struct {
Order *GetQuestionPageOrder `json:"order" form:"order" label:"排序"`
}
// GetQuestionPageOrder 获取题目列表-分页-排序条件
// GetQuestionPageOrder 获取问答题库列表-分页-排序条件
type GetQuestionPageOrder struct {
UpdatedAt string `json:"updated_at" form:"updated_at" label:"排序" validate:"oneof=desc asc"`
UpdatedAt string `json:"updated_at" form:"updated_at" label:"排序"`
}
// AddQuestion 新增题目
@ -96,3 +97,8 @@ type PutQuestionTest struct {
Input4 *string `json:"input4" form:"input4" label:"选项4"`
Input5 *string `json:"input5" form:"input5" label:"选项5"`
}
// DeleteQuestion 删除题目
type DeleteQuestion struct {
QuestionId []string `json:"question_id" form:"question_id" validate:"required" label:"题目id"`
}

View File

@ -0,0 +1,82 @@
package requests
type QuestionQaRequest struct {
GetQuestionQaPage // 获取问答题库列表-分页
AddQuestionQa // 新增问答题库
PutQuestionQa // 修改问答题库
PutQuestionQaPassword // 修改问答题库密码
PutQuestionQaExpire // 修改问答题库有效期
}
// GetQuestionQaPage 获取问答题库列表-分页
type GetQuestionQaPage 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"`
QaName string `json:"qa_name" form:"qa_name" label:"名称"`
QaStatus *int `json:"qa_status" form:"qa_status" label:"状态"` // 1:正常 2:过期)
QaRuleContent string `json:"qa_rule_content" form:"qa_rule_content" label:"规则解释"`
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转换为字符串格式处理
Order *GetQuestionQaPageOrder `json:"order" form:"order" label:"排序"`
}
// GetQuestionQaPageOrder 获取题目列表-分页-排序条件
type GetQuestionQaPageOrder struct {
UpdatedAt string `json:"updated_at" form:"updated_at" label:"排序"`
}
// AddQuestionQa 新增问答题库
type AddQuestionQa struct {
QaName string `json:"qa_name" form:"qa_name" label:"名称" validate:"required"`
QaRuleContent string `json:"qa_rule_content" form:"qa_rule_content" label:"规则解释" validate:"required"`
QaQuantity int `json:"qa_quantity" form:"qa_quantity" label:"题目数量" validate:"required,number,min=1"`
QaDisplayType int `json:"qa_display_type" form:"qa_display_type" label:"展示类型" validate:"required,oneof=1 2"` // 1:常规 2:飞花令)
QaExpireTime string `json:"qa_expire_time" form:"qa_expire_time" label:"过期时间" validate:"required"` // 注意这里假设LocalTime转换为字符串格式处理
QaPassword string `json:"qa_password" form:"qa_password" label:"分享密码" validate:"required"`
Image string `json:"image" form:"image" label:"背景图" validate:"required"`
Item []AddQuestionQaItem `json:"item" form:"item" label:"明细" validate:"required"`
}
// AddQuestionQaItem 新增问答题库-明细
type AddQuestionQaItem struct {
QuestionType int `json:"question_type" form:"question_type" validate:"required,number,oneof=1 2 3 4" label:"题目类型"` // 题目类型(1:单选 2:多选 3:问答 4:判断)
FirstLabelId string `json:"first_label_id" form:"first_label_id" validate:"required" label:"一级标签id"`
SecondLabelId string `json:"second_label_id" form:"second_label_id" label:"二级标签id"`
Difficulty *int `json:"difficulty" form:"difficulty" validate:"required" label:"难度"`
Quantity int `json:"quantity" form:"quantity" validate:"required,number,min=1" label:"数量"`
}
// PutQuestionQa 修改问答题库
type PutQuestionQa struct {
QaName string `json:"qa_name" form:"qa_name" label:"名称" validate:"required"`
QaRuleContent string `json:"qa_rule_content" form:"qa_rule_content" label:"规则解释" validate:"required"`
QaQuantity int `json:"qa_quantity" form:"qa_quantity" label:"题目数量" validate:"required,number,min=1"`
QaDisplayType int `json:"qa_display_type" form:"qa_display_type" label:"展示类型" validate:"required,oneof=1 2"` // 1:常规 2:飞花令)
QaExpireTime string `json:"qa_expire_time" form:"qa_expire_time" label:"过期时间" validate:"required"` // 注意这里假设LocalTime转换为字符串格式处理
QaPassword string `json:"qa_password" form:"qa_password" label:"分享密码" validate:"required"`
Image string `json:"image" form:"image" label:"背景图" validate:"required"`
Item []AddQuestionQaItem `json:"item" form:"item" label:"明细" validate:"required"`
Action int `json:"action" form:"action" label:"动作" validate:"required,oneof=1 2"` // 1:正常修改 2重新生成题库
}
// PutQuestionQaItem 修改问答题库-明细
type PutQuestionQaItem struct {
QuestionType int `json:"question_type" form:"question_type" validate:"required,number,oneof=1 2 3 4" label:"题目类型"` // 题目类型(1:单选 2:多选 3:问答 4:判断)
FirstLabelId string `json:"first_label_id" form:"first_label_id" validate:"required" label:"一级标签id"`
SecondLabelId string `json:"second_label_id" form:"second_label_id" label:"二级标签id"`
Difficulty *int `json:"difficulty" form:"difficulty" validate:"required" label:"难度"`
Quantity int `json:"quantity" form:"quantity" validate:"required,number,min=1" label:"数量"`
}
// PutQuestionQaPassword 修改问答题库密码
type PutQuestionQaPassword struct {
QaPassword string `json:"qa_password" form:"qa_password" label:"分享密码" validate:"required"`
}
// PutQuestionQaExpire 修改问答题库有效期
type PutQuestionQaExpire struct {
QaExpireTime string `json:"qa_expire_time" form:"qa_expire_time" label:"过期时间" validate:"required"` // 注意这里假设LocalTime转换为字符串格式处理
}

View File

@ -85,9 +85,6 @@ func publicRouter(r *gin.Engine, api controller.Api) {
// 登陆
adminGroup.POST("/login", api.AdminUser.Login)
// 新增题目
adminGroup.POST("/login2", api.AdminUser.Login)
// 新增题目
adminGroup.POST("/question/test", api.Question.AddQuestionTest)
@ -120,6 +117,28 @@ func privateRouter(r *gin.Engine, api controller.Api) {
// 修改题目
questionGroup.PUT("/:question_id", api.Question.PutQuestion)
// 删除题目
questionGroup.DELETE("", api.Question.DeleteQuestion)
}
// 问答
qaGroup := adminGroup.Group("/qa")
{
// 获取问答题库列表-分页
qaGroup.POST("/page", api.QuestionQa.GetQuestionQaPage)
// 新增问答题库
qaGroup.POST("", api.QuestionQa.AddQuestionQa)
// 修改问答题库
qaGroup.PUT("/:qa_id", api.Question.PutQuestionQa)
// 修改问答题库密码
qaGroup.PUT("/password/:qa_id", api.Question.PutQuestionQaPassword)
// 修改问答题库有效期
qaGroup.PUT("/expire/:qa_id", api.Question.PutQuestionQaExpire)
}
}

View File

@ -119,7 +119,7 @@ func (r *QuestionService) AddQuestion(req requests.AddQuestion) (bool, error) {
}
}
//tx.Commit()
tx.Commit()
return true, nil
}
@ -452,7 +452,7 @@ func (r *QuestionService) PutQuestion(questionId int64, req requests.PutQuestion
return false, errors.New(err.Error())
}
//tx.Commit()
tx.Commit()
return true, nil
}
@ -700,3 +700,49 @@ func (r *QuestionService) PutQuestionTest(questionId int64, req requests.PutQues
tx.Commit()
return true, nil
}
// DeleteQuestion 删除题目
func (r *QuestionService) DeleteQuestion(req requests.DeleteQuestion) (bool, error) {
questionDao := dao.QuestionDao{}
questionOptionDao := dao.QuestionOptionDao{}
// 开始事务
tx := global.Db.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
for _, questionId := range req.QuestionId {
// 将字符串转换为int64类型
questionId, err := strconv.ParseInt(questionId, 10, 64)
if err != nil {
tx.Rollback()
return false, errors.New("删除失败")
}
_, err = questionDao.GetQuestionById(questionId)
if err != nil {
tx.Rollback()
return false, errors.New("题目不存在")
}
// 删除选项
err = questionOptionDao.DeleteQuestionOptionByQuestionId(tx, questionId)
if err != nil {
tx.Rollback()
return false, errors.New(err.Error())
}
err = questionDao.DeleteQuestionById(tx, questionId)
if err != nil {
tx.Rollback()
return false, errors.New("删除失败")
}
}
tx.Commit()
return true, nil
}

420
api/service/QuestionQa.go Normal file
View File

@ -0,0 +1,420 @@
package service
import (
"errors"
"knowledge/api/dao"
"knowledge/api/model"
"knowledge/api/requests"
"knowledge/global"
"knowledge/utils"
"strconv"
"time"
)
type QuestionQaService struct {
}
// AddQuestionQa 新增问答题库
func (r *QuestionQaService) AddQuestionQa(req requests.AddQuestionQa) (bool, error) {
// 处理图片
if req.Image != "" {
req.Image = utils.RemoveOssDomain(req.Image)
}
// 验证过期时间
now := time.Now()
// 1:绝对时效
// 获取本地时区
location, err := time.LoadLocation("Local")
if err != nil {
return false, errors.New("新增失败")
}
qaExpireTime, err := time.ParseInLocation("2006-01-02 15:04", req.QaExpireTime, location)
if err != nil {
return false, errors.New("过期时间错误")
}
if qaExpireTime.Before(now) {
return false, errors.New("过期时间需大于当前时间")
}
// 生成分享链接
qaLink := ""
// 开始事务
tx := global.Db.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
// 新增题目
questionQa := &model.QuestionQa{
QaName: req.QaName,
QaQuantity: req.QaQuantity,
QaStatus: 1,
QaRuleContent: req.QaRuleContent,
QaDisplayType: req.QaDisplayType,
QaExpireTime: model.LocalTime(qaExpireTime),
QaLink: qaLink,
QaPassword: req.QaPassword,
OpenNumber: 0,
Image: req.Image,
}
questionQaDao := dao.QuestionQaDao{}
questionQa, err = questionQaDao.AddQuestionQa(tx, questionQa)
if err != nil {
tx.Rollback()
return false, errors.New("新增失败")
}
// 新增问答题库题目列表
questionDao := dao.QuestionDao{}
questionQaItemDao := dao.QuestionQaItemDao{}
for _, item := range req.Item {
// 验证一级标签
firstLabelId, err := strconv.ParseInt(item.FirstLabelId, 10, 64)
if err != nil {
tx.Rollback()
return false, err
}
labelDao := dao.LabelDao{}
_, err = labelDao.GetLabelFirstById(firstLabelId)
if err != nil {
tx.Rollback()
return false, err
}
// 验证二级标签
if item.SecondLabelId != "" {
secondLabelId, err := strconv.ParseInt(item.SecondLabelId, 10, 64)
if err != nil {
tx.Rollback()
return false, err
}
_, err = labelDao.GetLabelFirstById(secondLabelId)
if err != nil {
tx.Rollback()
return false, err
}
}
// 验证数量
maps := make(map[string]interface{})
maps["question_type"] = item.QuestionType
maps["question_status"] = 1
maps["is_delete"] = 0
maps["question_source"] = 1
if item.Difficulty != nil {
maps["difficulty"] = item.Difficulty
}
maps["first_label_id"] = item.FirstLabelId
if item.SecondLabelId != "" {
maps["second_label_id"] = item.SecondLabelId
}
total, err := questionDao.GetQuestionCount(maps)
if err != nil {
tx.Rollback()
return false, err
}
if total < int64(item.Quantity) {
tx.Rollback()
return false, errors.New("选题超出数量")
}
// 获取随机明细题目
questions, err := questionDao.GetQuestionListRand(maps, req.QaQuantity)
if err != nil {
tx.Rollback()
return false, err
}
for _, question := range questions {
questionQaItem := &model.QuestionQaItem{
QaId: questionQa.QaId,
QuestionId: question.QuestionId,
}
questionQaItem, err := questionQaItemDao.AddQuestionQaItem(tx, questionQaItem)
if err != nil {
tx.Rollback()
return false, errors.New("新增失败")
}
}
}
tx.Commit()
return true, nil
}
// PutQuestionQa 修改题目题库
func (r *QuestionQaService) PutQuestionQa(qaId int64, req requests.PutQuestionQa) (bool, error) {
questionQaDao := dao.QuestionQaDao{}
questionDao := dao.QuestionDao{}
questionQa, err := questionQaDao.GetQuestionQaById(qaId)
if err != nil {
return false, errors.New("题库不存在")
}
if questionQa.QaStatus == 2 {
return false, errors.New("题库已失效")
}
// 开始事务
tx := global.Db.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
questionQaData := make(map[string]interface{})
// 名称
if req.QaName != questionQa.QaName {
questionQaData["qa_name"] = req.QaName
}
// 规则解释
if req.QaRuleContent != questionQa.QaRuleContent {
questionQaData["qa_rule_content"] = req.QaRuleContent
}
// 展示类型
if req.QaDisplayType != questionQa.QaDisplayType {
questionQaData["qa_display_type"] = req.QaDisplayType
}
// 验证过期时间
now := time.Now()
// 1:绝对时效
// 获取本地时区
location, err := time.LoadLocation("Local")
if err != nil {
tx.Rollback()
return false, errors.New("新增失败")
}
qaExpireTime, err := time.ParseInLocation("2006-01-02 15:04", req.QaExpireTime, location)
if err != nil {
tx.Rollback()
return false, errors.New("过期时间错误")
}
if qaExpireTime.Before(now) {
tx.Rollback()
return false, errors.New("过期时间需大于当前时间")
}
// 分享密码
if req.QaPassword != questionQa.QaPassword {
questionQaData["qa_password"] = req.QaPassword
}
// 背景图
image := utils.RemoveOssDomain(req.Image)
if image != questionQa.Image {
questionQaData["image"] = image
}
// 题目数量
if req.QaQuantity != questionQa.QaQuantity {
if req.Action == 1 {
tx.Rollback()
return false, errors.New("您修改了题目数量,无法正常修改该题库,请重新生成")
}
questionQaData["qa_quantity"] = req.QaQuantity
}
// 处理题库明细-需重新生成时才会检测明细
if req.Action == 2 {
// 删除所有明细
questionQaItemDao := dao.QuestionQaItemDao{}
err := questionQaItemDao.DeleteQuestionQaItemByQaId(tx, questionQa.QaId)
if err != nil {
tx.Rollback()
return false, err
}
for _, item := range req.Item {
// 验证一级标签
firstLabelId, err := strconv.ParseInt(item.FirstLabelId, 10, 64)
if err != nil {
tx.Rollback()
return false, err
}
labelDao := dao.LabelDao{}
_, err = labelDao.GetLabelFirstById(firstLabelId)
if err != nil {
tx.Rollback()
return false, err
}
// 验证二级标签
if item.SecondLabelId != "" {
secondLabelId, err := strconv.ParseInt(item.SecondLabelId, 10, 64)
if err != nil {
tx.Rollback()
return false, err
}
_, err = labelDao.GetLabelFirstById(secondLabelId)
if err != nil {
tx.Rollback()
return false, err
}
}
// 验证数量
maps := make(map[string]interface{})
maps["question_type"] = item.QuestionType
maps["question_status"] = 1
maps["is_delete"] = 0
maps["question_source"] = 1
if item.Difficulty != nil {
maps["difficulty"] = item.Difficulty
}
maps["first_label_id"] = item.FirstLabelId
if item.SecondLabelId != "" {
maps["second_label_id"] = item.SecondLabelId
}
total, err := questionDao.GetQuestionCount(maps)
if err != nil {
tx.Rollback()
return false, err
}
if total < int64(item.Quantity) {
tx.Rollback()
return false, errors.New("选题超出数量")
}
// 获取随机明细题目
questions, err := questionDao.GetQuestionListRand(maps, req.QaQuantity)
if err != nil {
tx.Rollback()
return false, err
}
for _, question := range questions {
questionQaItem := &model.QuestionQaItem{
QaId: questionQa.QaId,
QuestionId: question.QuestionId,
}
questionQaItem, err := questionQaItemDao.AddQuestionQaItem(tx, questionQaItem)
if err != nil {
tx.Rollback()
return false, errors.New("新增失败")
}
}
}
}
if len(questionQaData) > 0 {
err = questionQaDao.EditQuestionQaById(tx, questionQa.QaId, questionQaData)
if err != nil {
tx.Rollback()
return false, errors.New(err.Error())
}
}
tx.Commit()
return true, nil
}
// PutQuestionQaPassword 修改题目题库密码
func (r *QuestionQaService) PutQuestionQaPassword(qaId int64, req requests.PutQuestionQaPassword) (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.QaPassword == questionQa.QaPassword {
return true, nil
}
// 开始事务
tx := global.Db.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
questionQaData := make(map[string]interface{})
questionQaData["qa_password"] = req.QaPassword
err = questionQaDao.EditQuestionQaById(tx, questionQa.QaId, questionQaData)
if err != nil {
tx.Rollback()
return false, errors.New(err.Error())
}
tx.Commit()
return true, nil
}
// PutQuestionQaExpire 修改问答题库有效期
func (r *QuestionQaService) PutQuestionQaExpire(qaId int64, req requests.PutQuestionQaExpire) (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("题库已失效")
}
// 验证过期时间
now := time.Now()
// 获取本地时区
location, err := time.LoadLocation("Local")
if err != nil {
return false, errors.New("修改失败")
}
qaExpireTime, err := time.ParseInLocation("2006-01-02 15:04", req.QaExpireTime, location)
if err != nil {
return false, errors.New("过期时间错误")
}
if qaExpireTime.Before(now) {
return false, errors.New("过期时间需大于当前时间")
}
// 开始事务
tx := global.Db.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
questionQaData := make(map[string]interface{})
questionQaData["qa_expire_time"] = qaExpireTime
err = questionQaDao.EditQuestionQaById(tx, questionQa.QaId, questionQaData)
if err != nil {
tx.Rollback()
return false, errors.New(err.Error())
}
tx.Commit()
return true, nil
}

21
core/cron.go Normal file
View File

@ -0,0 +1,21 @@
package core
import (
"fmt"
"github.com/robfig/cron/v3"
"knowledge/api/crontab"
)
func StartCron() {
c := cron.New(cron.WithSeconds())
_, err := c.AddFunc("0 * * * * *", crontab.HandleQuestionQaExpire)
if err != nil {
panic("定时器启动失败:" + err.Error())
}
// 启动定时任务调度器
c.Start()
fmt.Println("初始化定时器成功......")
}

1
go.mod
View File

@ -58,6 +58,7 @@ require (
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/richardlehane/mscfb v1.0.4 // indirect
github.com/richardlehane/msoleps v1.0.3 // indirect
github.com/robfig/cron/v3 v3.0.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect

2
go.sum
View File

@ -112,6 +112,8 @@ github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7
github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
github.com/richardlehane/msoleps v1.0.3 h1:aznSZzrwYRl3rLKRT3gUk9am7T/mLNSnJINvN0AQoVM=
github.com/richardlehane/msoleps v1.0.3/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
github.com/robfig/cron/v3 v3.0.0 h1:kQ6Cb7aHOHTSzNVNEhmp8EcWKLb4CbiMW9h9VyIhO4E=
github.com/robfig/cron/v3 v3.0.0/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=

View File

@ -30,6 +30,9 @@ func main() {
// 加载雪花算法
core.Snowflake()
// 加载定时器
core.StartCron()
// 初始化路由-加载中间件
r := router.Init()