diff --git a/api/problem.go b/api/problem.go new file mode 100644 index 0000000..faa977a --- /dev/null +++ b/api/problem.go @@ -0,0 +1,33 @@ +package api + +import ( + "github.com/gin-gonic/gin" + "online_code/core" + "online_code/models/param" + "online_code/repository" + "online_code/utils" +) + +type problemApi struct{} + +func ProblemApi() *problemApi { + return &problemApi{} +} + +func (problemApi) GetProbleList(ctx *gin.Context) { + var p param.GetProblemList + if err := ctx.ShouldBind(&p); err != nil { + core.R(ctx).FailWithMessage("参数错误: " + err.Error()) + return + } + + records, count, err := repository.ProblemService().GetList(p) + if err != nil { + core.R(ctx).FailWithMessage("获取题目列表失败: " + err.Error()) + return + } + // 计算总页码 + totalPage := utils.GenTotalPage(count, p.Size) + // 返回结果 + core.R(ctx).OkWithData(core.PageData{Current: p.Current, Size: p.Size, Total: count, TotalPage: totalPage, Records: records}) +} diff --git a/config.yaml b/config.yaml index e69de29..ce2cfc9 100644 --- a/config.yaml +++ b/config.yaml @@ -0,0 +1,19 @@ +app: + port: 8083 + name: online_code + version: v1 + prefix: /api/v1 + + +mysql: + host: 127.0.0.1 + port: 3306 + user: root + password: root + db: online_code + +redis: + host: 127.0.0.1 + port: 6379 + password: loser123 + db: 0 \ No newline at end of file diff --git a/define/define.go b/define/define.go new file mode 100644 index 0000000..409e1b8 --- /dev/null +++ b/define/define.go @@ -0,0 +1,6 @@ +package define + +var ( + DefaultCurrent = "1" + DefaultSize = "10" +) diff --git a/go.mod b/go.mod index 9b82d26..206d81b 100644 --- a/go.mod +++ b/go.mod @@ -5,13 +5,14 @@ go 1.18 require ( git.echol.cn/loser/logger v1.0.14 git.echol.cn/loser/nacos-viper-remote v0.4.1-0.20220525104600-e38430672884 - gitee.ltd/lxh/logger v1.0.14 github.com/caarlos0/env/v6 v6.9.2 github.com/fsnotify/fsnotify v1.5.1 github.com/gin-gonic/gin v1.7.7 github.com/go-redis/redis/v8 v8.11.5 github.com/google/uuid v1.1.2 github.com/spf13/viper v1.10.1 + golang.org/x/crypto v0.0.0-20220214200702-86341886e292 + golang.org/x/sync v0.0.0-20210220032951-036812b2e83c gorm.io/driver/mysql v1.3.2 gorm.io/gorm v1.23.5 ) @@ -19,6 +20,7 @@ require ( require ( cloud.google.com/go v0.99.0 // indirect cloud.google.com/go/firestore v1.6.1 // indirect + gitee.ltd/lxh/logger v1.0.14 // indirect github.com/aliyun/alibaba-cloud-sdk-go v1.61.18 // indirect github.com/armon/go-metrics v0.3.10 // indirect github.com/beorn7/perks v1.0.1 // indirect @@ -96,7 +98,6 @@ require ( go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.8.0 // indirect go.uber.org/zap v1.21.0 // indirect - golang.org/x/crypto v0.0.0-20210915214749-c084706c2272 // indirect golang.org/x/net v0.0.0-20220517181318-183a9ca12b87 // indirect golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 // indirect golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e // indirect diff --git a/initialize/db_table.go b/initialize/db_table.go index 02ba293..cdd1529 100644 --- a/initialize/db_table.go +++ b/initialize/db_table.go @@ -9,7 +9,12 @@ import ( // 初始化数据库表 func databaseTable() { dbs := []any{ - new(entity.User), // 管理员用户 + new(entity.User), + new(entity.Category), + new(entity.Problem), + new(entity.ProblemCategory), + new(entity.Submit), + new(entity.TestCase), } if err := client.MySQL.AutoMigrate(dbs...); err != nil { diff --git a/main.go b/main.go new file mode 100644 index 0000000..ea5b90f --- /dev/null +++ b/main.go @@ -0,0 +1,50 @@ +package main + +import ( + "fmt" + "git.echol.cn/loser/logger/log" + "github.com/gin-gonic/gin" + "golang.org/x/sync/errgroup" + "net/http" + "online_code/config" + "online_code/core" + "online_code/route" + "time" +) + +var g errgroup.Group + +// 启动入口 +func main() { + // 强制日志颜色化 + gin.ForceConsoleColor() + app := &http.Server{ + Addr: fmt.Sprintf(":%d", config.Scd.Api.Port), + Handler: api(), + ReadTimeout: 5 * time.Second, + WriteTimeout: 1 * time.Minute, + } + + // 启动项目 + g.Go(func() error { + return app.ListenAndServe() + }) + if err := g.Wait(); err != nil { + log.Panicf("启动失败,错误信息:%s", err.Error()) + } +} + +// 生成接口服务 +func api() http.Handler { + app := gin.New() + app.Use(gin.Recovery()) + // 开启自定义请求方式不允许处理函数 + app.HandleMethodNotAllowed = true + // 处理请求方式不对 + app.NoMethod(core.NoMethodHandler()) + // 404返回数据 + app.NoRoute(core.NoRouteHandler()) + // 初始化路由 + route.InitRoute(app.Group(config.Scd.Api.Prefix)) + return app +} diff --git a/models/cache/user.go b/models/cache/user.go new file mode 100644 index 0000000..d40efc4 --- /dev/null +++ b/models/cache/user.go @@ -0,0 +1,18 @@ +package cache + +import "encoding/json" + +// UserInfo 登录用的用户信息结构体 +type UserInfo struct { + UserType string `json:"userType"` // 用户类型 + UserId string `json:"userId"` // 用户Id +} + +// String 实现Stringer接口 +func (i UserInfo) String() (string, error) { + b, err := json.Marshal(i) + if err != nil { + return "", err + } + return string(b), nil +} diff --git a/models/entity/problem_category.go b/models/entity/problem_category.go index d2a5fef..6b758b5 100644 --- a/models/entity/problem_category.go +++ b/models/entity/problem_category.go @@ -4,7 +4,7 @@ import ( "online_code/common/types" ) -// ProblemCategory 问题分类表 +// ProblemCategory 问题分类关联表 type ProblemCategory struct { types.BaseDbModel ProblemId uint `json:"problem_id" gorm:"column:problem_id;type:int(11);comment:'问题的ID'" ` // 问题的ID diff --git a/models/param/base.go b/models/param/base.go new file mode 100644 index 0000000..c18ba04 --- /dev/null +++ b/models/param/base.go @@ -0,0 +1,7 @@ +package param + +// 分页通用参数 +type page struct { + Current int `json:"current" form:"current" binding:"required"` // 页码 + Size int `json:"size" form:"size" binding:"required"` // 每页数量 +} diff --git a/models/param/problem.go b/models/param/problem.go new file mode 100644 index 0000000..872d1b8 --- /dev/null +++ b/models/param/problem.go @@ -0,0 +1,6 @@ +package param + +type GetProblemList struct { + page + Keyword string `json:"keyword" form:"keyword"` // 问题关键字 +} diff --git a/repository/base.go b/repository/base.go new file mode 100644 index 0000000..ea535fa --- /dev/null +++ b/repository/base.go @@ -0,0 +1,19 @@ +package repository + +import "gorm.io/gorm" + +// 分页组件 +func page(current, size int) func(db *gorm.DB) *gorm.DB { + return func(db *gorm.DB) *gorm.DB { + if current == 0 { + current = 1 + } + if size < 1 { + size = 10 + } + // 计算偏移量 + offset := (current - 1) * size + // 返回组装结果 + return db.Offset(offset).Limit(size) + } +} diff --git a/repository/problem.go b/repository/problem.go new file mode 100644 index 0000000..f9174de --- /dev/null +++ b/repository/problem.go @@ -0,0 +1,31 @@ +package repository + +import ( + "online_code/client" + "online_code/models/entity" + "online_code/models/param" +) + +type problemService struct{} + +func ProblemService() *problemService { + return &problemService{} +} + +// GetList 获取题目列表 +func (problemService) GetList(p param.GetProblemList) (records entity.Problem, count int64, err error) { + sel := client.MySQL.Scopes(page(p.Current, p.Size)) + + if p.Keyword != "" { + sel.Where("title LIKE ? OR content like ?", "%"+p.Keyword+"%", "%"+p.Keyword+"%") + } + + err = sel.Order("updated_at DESC").Find(&records).Offset(-1).Limit(-1).Count(&count).Error + return +} + +// GetProblemInfo 获取题目详情 +func (problemService) GetProblemInfo(id int) (problem entity.Problem, err error) { + err = client.MySQL.Where("id = ?", id).Find(&problem).Error + return +} diff --git a/route/problem.go b/route/problem.go new file mode 100644 index 0000000..2a403d6 --- /dev/null +++ b/route/problem.go @@ -0,0 +1,10 @@ +package route + +import ( + "github.com/gin-gonic/gin" + "online_code/api" +) + +func problem(g *gin.RouterGroup) { + g.GET("/list", api.ProblemApi().GetProbleList) +} diff --git a/route/router.go b/route/router.go new file mode 100644 index 0000000..06e3117 --- /dev/null +++ b/route/router.go @@ -0,0 +1,8 @@ +package route + +import "github.com/gin-gonic/gin" + +func InitRoute(g *gin.RouterGroup) { + api := g.Group("/api") + problem(api.Group("/problem")) +} diff --git a/utils/page.go b/utils/page.go new file mode 100644 index 0000000..db82082 --- /dev/null +++ b/utils/page.go @@ -0,0 +1,14 @@ +package utils + +// GenTotalPage 计算总页数 +func GenTotalPage(count int64, size int) int { + totalPage := 0 + if count > 0 { + upPage := 0 + if int(count)%size > 0 { + upPage = 1 + } + totalPage = (int(count) / size) + upPage + } + return totalPage +} diff --git a/utils/token.go b/utils/token.go new file mode 100644 index 0000000..1b9ff02 --- /dev/null +++ b/utils/token.go @@ -0,0 +1,20 @@ +package utils + +import ( + "golang.org/x/crypto/bcrypt" +) + +// 生成Token的密钥,写死,防止乱改 +//var jwtSecret = "qSxw4fCBBBecPsws" + +// HashPassword 加密密码 +func HashPassword(pass *string) { + bytePass := []byte(*pass) + hPass, _ := bcrypt.GenerateFromPassword(bytePass, bcrypt.DefaultCost) + *pass = string(hPass) +} + +// ComparePassword 校验密码 +func ComparePassword(dbPass, pass string) bool { + return bcrypt.CompareHashAndPassword([]byte(dbPass), []byte(pass)) == nil +}