Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable searching courses by teacher name #142

Merged
merged 10 commits into from
Dec 19, 2024
8 changes: 7 additions & 1 deletion danke/api/course_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package api

import (
"errors"

"github.com/gofiber/fiber/v2"
. "github.com/opentreehole/backend/common"
. "github.com/opentreehole/backend/danke/model"
Expand Down Expand Up @@ -148,7 +149,12 @@ func SearchCourseGroupV3(c *fiber.Ctx) (err error) {
if CourseCodeRegexp.MatchString(query) {
querySet = querySet.Where("code LIKE ?", query+"%")
} else {
querySet = querySet.Where("name LIKE ?", "%"+query+"%")
queryWord :="%"+query+"%"
querySet = querySet.
Joins("JOIN teacher_course_groups tcg ON course_group.id = tcg.course_group_id").
Joins("JOIN teacher t ON tc.teacher_id = t.id").
Where("t.name like ? OR course_group.name LIKE ?", queryWord, queryWord).
Group("id")
HydrogenC marked this conversation as resolved.
Show resolved Hide resolved
}
if page > 0 {
if pageSize == 0 {
Expand Down
6 changes: 4 additions & 2 deletions danke/model/course_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ package model
import (
"context"
"encoding/base64"
"regexp"
"time"

"github.com/eko/gocache/lib/v4/store"
"github.com/opentreehole/backend/common"
"github.com/vmihailenco/msgpack/v5"
"golang.org/x/crypto/sha3"
"regexp"
"time"
)

// CourseGroup 课程组
Expand All @@ -24,6 +25,7 @@ type CourseGroup struct {
CourseCount int `json:"course_count" gorm:"not null;default:0"` // 课程数量
ReviewCount int `json:"review_count" gorm:"not null;default:0"` // 评价数量
Courses CourseList `json:"courses"`
Teachers []*Teacher `gorm:"many2many:teacher_course_link;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
}

var CourseCodeRegexp = regexp.MustCompile(`^([A-Z]{3,})([0-9]{2,})`)
Expand Down
1 change: 1 addition & 0 deletions danke/model/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ func Init() {
err = DB.AutoMigrate(
&CourseGroup{},
&Course{},
&Teacher{},
&Review{},
&ReviewHistory{},
&Achievement{},
Expand Down
7 changes: 7 additions & 0 deletions danke/model/teacher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package model

type Teacher struct {
ID int `json:"id"`
Name string `json:"name" gorm:"not null"` // 教师姓名
CourseGroups []*CourseGroup `gorm:"many2many:teacher_course_link;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
}
60 changes: 60 additions & 0 deletions danke_utils/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package main

import (
"log"
"os"
"time"

"github.com/opentreehole/backend/common"
"github.com/spf13/viper"
"gorm.io/driver/mysql"
"gorm.io/driver/postgres"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"gorm.io/gorm/schema"
)

var GormConfig = &gorm.Config{
NamingStrategy: schema.NamingStrategy{
SingularTable: true, // 表名使用单数, `User` -> `user`
},
DisableForeignKeyConstraintWhenMigrating: true, // 禁用自动创建外键约束,必须手动创建或者在业务逻辑层维护
Logger: logger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags),
logger.Config{
SlowThreshold: time.Second, // 慢 SQL 阈值
LogLevel: logger.Error, // 日志级别
IgnoreRecordNotFoundError: true, // 忽略ErrRecordNotFound(记录未找到)错误
Colorful: false, // 禁用彩色打印
},
),
}

var DB *gorm.DB

func Init() {
viper.AutomaticEnv()
dbType := viper.GetString(common.EnvDBType)
dbUrl := viper.GetString(common.EnvDBUrl)

var err error

switch dbType {
case "mysql":
DB, err = gorm.Open(mysql.Open(dbUrl), GormConfig)
case "postgres":
DB, err = gorm.Open(postgres.Open(dbUrl), GormConfig)
default:
panic("db type not supported")
}

if err != nil {
panic(err)
}
}

func main() {
Init()
// Call any script as needed
// GenerateTeacherTable(DB)
}
94 changes: 94 additions & 0 deletions danke_utils/teacher_table.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package main

import (
"fmt"
"slices"
"strings"

"gorm.io/gorm"
"gorm.io/gorm/clause"
)

const (
BatchSize = 1000
)

type Course struct {
ID int `json:"id"`
Name string `json:"name" gorm:"not null"` // 课程名称
Code string `json:"code" gorm:"not null"` // 课程编号
CodeID string `json:"code_id" gorm:"not null"` // 选课序号。用于区分同一课程编号的不同平行班
Teachers string `json:"teachers" gorm:"not null"`
CourseGroupID int `json:"course_group_id" gorm:"not null;index"` // 课程组编号
}

type Teacher struct {
ID int
Name string `gorm:"not null"` // 课程组 ID
}

type TeacherCourseLink struct {
TeacherID int `gorm:"primaryKey;autoIncrement:false"`
CourseGroupID int `gorm:"primaryKey;autoIncrement:false"` // 课程组编号
}

func AppendUnique[T comparable](slice []T, elems ...T) []T {
for _, elem := range elems {
if !slices.Contains(slice, elem) {
slice = append(slice, elem)
}
}

return slice
}

func GenerateTeacherTable(DB *gorm.DB) {
Init()

// reader := bufio.NewReader(os.Stdin)

dataMap := map[string][]int{}

var queryResult []Course
query := DB.Table("course")
query.FindInBatches(&queryResult, BatchSize, func(tx *gorm.DB, batch int) error {
for _, course := range queryResult {
teacherList := strings.Split(course.Teachers, ",")
for _, name := range teacherList {
courseList, found := dataMap[name]
if found {
dataMap[name] = AppendUnique(courseList, course.CourseGroupID)
} else {
dataMap[name] = []int{course.CourseGroupID}
}
}
}

fmt.Printf("Handled batchg %d\n", batch)
return nil
})

var teachers []*Teacher
for k := range dataMap {
teachers = append(teachers, &Teacher{Name: k})
}

// Avoid insertion failure due to duplication
DB.Clauses(clause.OnConflict{DoNothing: true}).Table("teacher").Create(teachers)

var links []*TeacherCourseLink
for index, teacher := range teachers {
for _, cid := range dataMap[teacher.Name] {
links = append(links, &TeacherCourseLink{TeacherID: teacher.ID, CourseGroupID: cid})
}

// Submit every 100 teachers to avoid SQL being too long
if index%100 == 0 {
fmt.Printf("Inserted %d teachers\n", index)

// Avoid insertion failure due to duplication
DB.Clauses(clause.OnConflict{DoNothing: true}).Table("teacher_courses").Create(links)
links = nil
}
}
}