diff --git a/cmd/init_db/main.go b/cmd/init_db/main.go index 905b375..dfa05f6 100644 --- a/cmd/init_db/main.go +++ b/cmd/init_db/main.go @@ -13,6 +13,7 @@ func main() { err := db.AutoMigrate( &user_model.User{}, &problem_model.Problem{}, + &problem_model.ProblemInfo{}, &judge_model.Judge{}, &judge_model.JudgeResult{}, ) diff --git a/cmd/problem_loader/main.go b/cmd/problem_loader/main.go index f9a5e7f..6c30ed3 100644 --- a/cmd/problem_loader/main.go +++ b/cmd/problem_loader/main.go @@ -72,11 +72,19 @@ func main() { } description := string(content) log.AppLogger().WithField("description", description).Debug("Read problem.md") + err = problem_model.CreateProblem(db, problem_model.Problem{ Slug: slug, - Title: title, Description: &description, - Tags: []*problem_model.AlgorithmTag{ + }) + if err != nil { + return err + } + + err = problem_model.CreateProblemInfo(db, problem_model.ProblemInfo{ + ProblemSlug: slug, + Title: title, + Tags: []*problem_model.ProblemTag{ {Name: "to-be-add"}, }, }) diff --git a/cmd/web_server/handler/problem.go b/cmd/web_server/handler/problem.go index d4dfb2a..ce94859 100644 --- a/cmd/web_server/handler/problem.go +++ b/cmd/web_server/handler/problem.go @@ -46,7 +46,7 @@ func getProblem(ginCtx *gin.Context) { ginCtx.JSON(200, gin.H{ "slug": problemInfo.Slug, - "title": problemInfo.Title, + "title": problemInfo.Info.Title, "description": problemInfo.Description, "tags": problem_model.GetTagsList(*problemInfo), }) diff --git a/models/problem/problem.go b/models/problem/problem.go index fd914a9..c0856db 100644 --- a/models/problem/problem.go +++ b/models/problem/problem.go @@ -4,32 +4,9 @@ import "github.com/oj-lab/oj-lab-platform/models" type Problem struct { models.MetaFields - Slug string `gorm:"primaryKey" json:"slug"` - Title string `gorm:"not null" json:"title"` - Description *string `json:"description,omitempty"` - Tags []*AlgorithmTag `gorm:"many2many:problem_algorithm_tags;" json:"tags"` + Slug string `json:"slug" gorm:"primaryKey"` + Info ProblemInfo `json:"info"` + Description *string `json:"description,omitempty"` } -type AlgorithmTag struct { - models.MetaFields - Name string `gorm:"primaryKey" json:"name"` - Problems []*Problem `gorm:"many2many:problem_algorithm_tags;" json:"problems,omitempty"` -} - -type ProblemInfo struct { - models.MetaFields - Slug string `json:"slug"` - Title string `json:"title"` - Tags []*AlgorithmTag `json:"tags"` -} - -var ProblemInfoSelection = append([]string{"slug", "title"}, models.MetaFieldsSelection...) - -func (p Problem) ToProblemInfo() ProblemInfo { - return ProblemInfo{ - MetaFields: p.MetaFields, - Slug: p.Slug, - Title: p.Title, - Tags: p.Tags, - } -} +var InfoSelection = append([]string{"slug", "title"}, models.MetaFieldsSelection...) diff --git a/models/problem/problem_db.go b/models/problem/problem_db.go index 858590f..1351a43 100644 --- a/models/problem/problem_db.go +++ b/models/problem/problem_db.go @@ -2,7 +2,6 @@ package problem import ( "gorm.io/gorm" - "gorm.io/gorm/clause" ) func CreateProblem(tx *gorm.DB, problem Problem) error { @@ -11,7 +10,9 @@ func CreateProblem(tx *gorm.DB, problem Problem) error { func GetProblem(tx *gorm.DB, slug string) (*Problem, error) { db_problem := Problem{} - err := tx.Model(&Problem{}).Preload("Tags").Where("Slug = ?", slug).First(&db_problem).Error + err := tx.Model(&Problem{}).Preload("Info"). + Preload("Info.Tags"). + Where("slug = ?", slug).First(&db_problem).Error if err != nil { return nil, err } @@ -20,11 +21,15 @@ func GetProblem(tx *gorm.DB, slug string) (*Problem, error) { } func DeleteProblem(tx *gorm.DB, slug string) error { - var problem Problem - if err := tx.Where("slug = ?", slug).First(&problem).Error; err != nil { + err := tx.Model(&ProblemInfo{ + ProblemSlug: slug, + }).Association("Tags").Clear() + if err != nil { return err } - return tx.Select(clause.Associations).Delete(&problem).Error + + return tx.Select("Info"). + Delete(&Problem{Slug: slug}).Error } func UpdateProblem(tx *gorm.DB, problem Problem) error { @@ -32,76 +37,16 @@ func UpdateProblem(tx *gorm.DB, problem Problem) error { } type GetProblemOptions struct { - Selection []string - Slug *string - Title *string - Tags []*AlgorithmTag - Offset *int - Limit *int -} - -func buildGetProblemTXByOptions(tx *gorm.DB, options GetProblemOptions, isCount bool) *gorm.DB { - tagsList := []string{} - for _, tag := range options.Tags { - tagsList = append(tagsList, tag.Name) - } - tx = tx.Model(&Problem{}) - if len(options.Selection) > 0 { - tx = tx.Select(options.Selection) - } - if len(tagsList) > 0 { - tx = tx.Joins("JOIN problem_algorithm_tags ON problem_algorithm_tags.problem_slug = problems.slug"). - Where("problem_algorithm_tags.algorithm_tag_name in ?", tagsList) - } - if options.Slug != nil { - tx = tx.Where("slug = ?", *options.Slug) - } - if options.Title != nil { - tx = tx.Where("title = ?", *options.Title) - } - tx = tx.Distinct(). - Preload("Tags") - if !isCount { - if options.Offset != nil { - tx = tx.Offset(*options.Offset) - } - if options.Limit != nil { - tx = tx.Limit(*options.Limit) - } - } - - return tx -} - -func CountProblemByOptions(tx *gorm.DB, options GetProblemOptions) (int64, error) { - var count int64 - - tx = buildGetProblemTXByOptions(tx, options, true) - err := tx.Count(&count).Error - - return count, err -} - -func GetProblemListByOptions(tx *gorm.DB, options GetProblemOptions) ([]Problem, int64, error) { - total, err := CountProblemByOptions(tx, options) - if err != nil { - return nil, 0, err - } - - problemList := []Problem{} - - tx = buildGetProblemTXByOptions(tx, options, false) - err = tx.Find(&problemList).Error - if err != nil { - return nil, 0, err - } - - return problemList, total, nil + Slug *string + Title *string + Tags []*ProblemTag + Offset *int + Limit *int } func GetTagsList(problem Problem) []string { tagsList := []string{} - for _, tag := range problem.Tags { + for _, tag := range problem.Info.Tags { tagsList = append(tagsList, tag.Name) } return tagsList diff --git a/models/problem/problem_info.go b/models/problem/problem_info.go new file mode 100644 index 0000000..d00481d --- /dev/null +++ b/models/problem/problem_info.go @@ -0,0 +1,16 @@ +package problem + +import "github.com/oj-lab/oj-lab-platform/models" + +type ProblemInfo struct { + models.MetaFields + ProblemSlug string `json:"problem_slug" gorm:"primaryKey"` + Title string `json:"title"` + Tags []*ProblemTag `json:"tags" gorm:"many2many:problem_info_tags;"` +} + +type ProblemTag struct { + models.MetaFields + Name string `json:"name" gorm:"primaryKey"` + Infos []*ProblemInfo `json:"problem_infos" gorm:"many2many:problem_info_tags;"` +} diff --git a/models/problem/problem_info_db.go b/models/problem/problem_info_db.go new file mode 100644 index 0000000..e4b07b0 --- /dev/null +++ b/models/problem/problem_info_db.go @@ -0,0 +1,81 @@ +package problem + +import ( + "gorm.io/gorm" +) + +func CreateProblemInfo(tx *gorm.DB, problemInfo ProblemInfo) error { + return tx.Create(&problemInfo).Error +} + +func buildGetProblemInfoTXByOptions(tx *gorm.DB, options GetProblemOptions, isCount bool) *gorm.DB { + tagsList := []string{} + for _, tag := range options.Tags { + tagsList = append(tagsList, tag.Name) + } + tx = tx.Model(&ProblemInfo{}) + tx = tx.Distinct() + + if len(tagsList) > 0 { + tx.Preload("Tags", "name in ?", tagsList) + } else { + tx = tx.Preload("AlgorithmTags") + } + if options.Slug != nil { + tx = tx.Where("problem_slug = ?", *options.Slug) + } + if options.Title != nil { + tx = tx.Where("title = ?", *options.Title) + } + if !isCount { + if options.Offset != nil { + tx = tx.Offset(*options.Offset) + } + if options.Limit != nil { + tx = tx.Limit(*options.Limit) + } + } + + return tx +} + +func CountProblemInfoByOptions(tx *gorm.DB, options GetProblemOptions) (int64, error) { + var count int64 + + tx = buildGetProblemInfoTXByOptions(tx, options, true) + err := tx.Count(&count).Error + + return count, err +} + +func GetProblemInfoListByOptions( + tx *gorm.DB, options GetProblemOptions, +) ([]ProblemInfo, int64, error) { + total, err := CountProblemInfoByOptions(tx, options) + if err != nil { + return nil, 0, err + } + + problemList := []ProblemInfo{} + + tx = buildGetProblemInfoTXByOptions(tx, options, false) + err = tx.Find(&problemList).Error + if err != nil { + return nil, 0, err + } + + return problemList, total, nil +} + +func DeleteProblemInfo(tx *gorm.DB, slug string) error { + err := tx.Model(&ProblemInfo{ + ProblemSlug: slug, + }).Association("Tags").Clear() + if err != nil { + return err + } + + return tx.Delete(&ProblemInfo{ + ProblemSlug: slug, + }).Error +} diff --git a/services/problem/problem_info.go b/services/problem/problem_info.go index f5e859f..dd9b249 100644 --- a/services/problem/problem_info.go +++ b/services/problem/problem_info.go @@ -7,20 +7,17 @@ import ( gorm_agent "github.com/oj-lab/oj-lab-platform/modules/agent/gorm" ) -func getProblemInfoList(_ context.Context) ([]problem_model.ProblemInfo, int64, error) { +func getProblemInfoList( + _ context.Context, +) ([]problem_model.ProblemInfo, int64, error) { db := gorm_agent.GetDefaultDB() - getOptions := problem_model.GetProblemOptions{ - Selection: problem_model.ProblemInfoSelection, - } + getOptions := problem_model.GetProblemOptions{} - problemList, total, err := problem_model.GetProblemListByOptions(db, getOptions) + problemInfoList, total, err := + problem_model.GetProblemInfoListByOptions(db, getOptions) if err != nil { return nil, 0, err } - problemInfoList := []problem_model.ProblemInfo{} - for _, problem := range problemList { - problemInfoList = append(problemInfoList, problem.ToProblemInfo()) - } return problemInfoList, total, nil } diff --git a/tests/models/problem_test.go b/tests/models/problem_test.go index 6908b17..5fc395f 100644 --- a/tests/models/problem_test.go +++ b/tests/models/problem_test.go @@ -14,9 +14,7 @@ func TestProblemDB(t *testing.T) { description := "Given two integer A and B, please output the answer of A+B." problem := problem_model.Problem{ Slug: "a-plus-b-problem", - Title: "A+B Problem", Description: &description, - Tags: []*problem_model.AlgorithmTag{{Name: "tag1"}, {Name: "tag2"}}, } err := problem_model.CreateProblem(db, problem) @@ -24,6 +22,15 @@ func TestProblemDB(t *testing.T) { t.Error(err) } + err = problem_model.CreateProblemInfo(db, problem_model.ProblemInfo{ + ProblemSlug: problem.Slug, + Title: "A+B Problem", + Tags: []*problem_model.ProblemTag{{Name: "tag1"}, {Name: "tag2"}}, + }) + if err != nil { + t.Error(err) + } + dbProblem, err := problem_model.GetProblem(db, problem.Slug) if err != nil { t.Error(err) @@ -36,12 +43,12 @@ func TestProblemDB(t *testing.T) { fmt.Printf("%+v\n", string(problemJson)) problemOption := problem_model.GetProblemOptions{ - Selection: problem_model.ProblemInfoSelection, - Tags: []*problem_model.AlgorithmTag{{Name: "tag1"}}, - Slug: &problem.Slug, + Tags: []*problem_model.ProblemTag{{Name: "tag1"}}, + Slug: &problem.Slug, } - problemList, problemCount, err := problem_model.GetProblemListByOptions(db, problemOption) + problemList, problemCount, err := + problem_model.GetProblemInfoListByOptions(db, problemOption) if err != nil { t.Error(err) } @@ -56,6 +63,11 @@ func TestProblemDB(t *testing.T) { } fmt.Printf("%+v\n", string(problemListJson)) + err = problem_model.DeleteProblemInfo(db, problem.Slug) + if err != nil { + t.Error(err) + } + err = problem_model.DeleteProblem(db, problem.Slug) if err != nil { t.Error(err)