feat: add problem support

This commit is contained in:
Paul Pan 2022-09-26 16:13:31 +08:00
parent 8d01144d8b
commit 529b41332c
13 changed files with 197 additions and 12 deletions

View File

@ -0,0 +1,32 @@
package problem
import (
"github.com/WHUPRJ/woj-server/internal/global"
"github.com/WHUPRJ/woj-server/internal/service/problem"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
var _ Handler = (*handler)(nil)
type Handler interface {
Update(c *gin.Context)
Search(c *gin.Context)
}
type handler struct {
log *zap.Logger
problemService problem.Service
jwtService global.JwtService
}
func RouteRegister(g *global.Global, group *gin.RouterGroup) {
app := &handler{
log: g.Log,
problemService: problem.NewService(g),
jwtService: g.Jwt,
}
group.POST("/search", app.Search)
group.POST("/update", app.jwtService.Handler(), app.Update)
}

View File

@ -0,0 +1,44 @@
package problem
import (
"github.com/WHUPRJ/woj-server/internal/e"
"github.com/gin-gonic/gin"
)
type searchRequest struct {
Pid uint `form:"pid"`
Search string `form:"search"`
}
// Search
// @Summary get detail of a problem
// @Description get detail of a problem
// @Accept application/x-www-form-urlencoded
// @Produce json
// @Param pid formData int false "problem id"
// @Param search formData string false "search problem"
// @Response 200 {object} e.Response "problem info"
// @Router /v1/problem/search [post]
func (h *handler) Search(c *gin.Context) {
req := new(searchRequest)
if err := c.ShouldBind(req); err != nil {
e.Pong(c, e.InvalidParameter, err.Error())
return
}
if req.Pid == 0 && req.Search == "" {
e.Pong(c, e.InvalidParameter, nil)
return
}
if req.Pid != 0 {
problem, status := h.problemService.Query(req.Pid)
e.Pong(c, status, problem)
return
} else {
problem, status := h.problemService.QueryFuzz(req.Search)
e.Pong(c, status, problem)
return
}
}

View File

@ -0,0 +1,81 @@
package problem
import (
"github.com/WHUPRJ/woj-server/internal/e"
"github.com/WHUPRJ/woj-server/internal/global"
"github.com/WHUPRJ/woj-server/internal/repo/model"
"github.com/gin-gonic/gin"
)
type updateRequest struct {
Pid uint `form:"pid"`
Title string `form:"title" binding:"required"`
Content string `form:"content" binding:"required"`
TimeLimit uint `form:"time_limit" binding:"required"`
MemoryLimit uint `form:"memory_limit" binding:"required"`
IsEnabled bool `form:"is_enabled"`
}
// Update
// @Summary create or update a problem
// @Description create or update a problem
// @Accept application/x-www-form-urlencoded
// @Produce json
// @Param pid formData int false "problem id, 0 for create"
// @Param title formData string true "title"
// @Param content formData string true "content"
// @Param time_limit formData int true "time limit in ms"
// @Param memory_limit formData int true "memory limit in kb"
// @Param is_enabled formData bool false "is enabled"
// @Response 200 {object} e.Response "problem info without provider information"
// @Security Authentication
// @Router /v1/problem/update [post]
func (h *handler) Update(c *gin.Context) {
claim, exist := c.Get("claim")
if !exist {
e.Pong(c, e.UserUnauthenticated, nil)
return
}
uid := claim.(*global.Claim).UID
role := claim.(*global.Claim).Role
if role < model.RoleAdmin {
e.Pong(c, e.UserUnauthorized, nil)
return
}
req := new(updateRequest)
if err := c.ShouldBind(req); err != nil {
e.Pong(c, e.InvalidParameter, err.Error())
return
}
problem := &model.Problem{
Title: req.Title,
Content: req.Content,
TimeLimit: req.TimeLimit,
MemoryLimit: req.MemoryLimit,
IsEnabled: req.IsEnabled,
}
if req.Pid == 0 {
problem, status := h.problemService.Create(uid, problem)
e.Pong(c, status, problem)
return
} else {
inDb, status := h.problemService.Query(req.Pid)
if status != e.Success && status != e.ProblemNotAvailable {
e.Pong(c, status, nil)
return
}
if inDb.ProviderID != uid {
e.Pong(c, e.UserUnauthorized, nil)
return
}
problem, status := h.problemService.Update(req.Pid, problem)
e.Pong(c, status, problem)
return
}
}

View File

@ -25,7 +25,7 @@ type handler struct {
func RouteRegister(g *global.Global, group *gin.RouterGroup) {
app := &handler{
log: g.Log,
userService: user.NewUserService(g),
userService: user.NewService(g),
jwtService: g.Jwt,
}

View File

@ -16,7 +16,7 @@ type profileRequest struct {
// @Description fetch user profile
// @Accept application/x-www-form-urlencoded
// @Produce json
// @Param uid formData string false "user id"
// @Param uid formData int false "user id"
// @Response 200 {object} e.Response "user info"
// @Security Authentication
// @Router /v1/user/profile [post]

View File

@ -25,7 +25,8 @@ const (
UserUnauthorized Status = 304
UserDisabled Status = 305
ProblemNotFound Status = 500
ProblemNotFound Status = 500
ProblemNotAvailable Status = 501
)
var msgText = map[Status]string{
@ -53,5 +54,6 @@ var msgText = map[Status]string{
UserUnauthorized: "User Unauthorized",
UserDisabled: "User Disabled",
ProblemNotFound: "Problem Not Found",
ProblemNotFound: "Problem Not Found",
ProblemNotAvailable: "Problem Not Available",
}

View File

@ -3,12 +3,12 @@ package model
import "gorm.io/gorm"
type Problem struct {
gorm.Model `json:"-"`
gorm.Model `json:"meta"`
Title string `json:"title" gorm:"not null"`
Content string `json:"content" gorm:"not null"`
TimeLimit uint `json:"time_limit" gorm:"not null"`
MemoryLimit uint `json:"memory_limit" gorm:"not null"`
ProviderID uint `json:"provider_id" gorm:"not null;index"`
Provider User `json:"provider" gorm:"foreignKey:ProviderID"`
Provider User `json:"-" gorm:"foreignKey:ProviderID"`
IsEnabled bool `json:"is_enabled" gorm:"not null;index"`
}

View File

@ -2,6 +2,7 @@ package router
import (
"github.com/WHUPRJ/woj-server/internal/api/debug"
"github.com/WHUPRJ/woj-server/internal/api/problem"
"github.com/WHUPRJ/woj-server/internal/api/user"
"github.com/WHUPRJ/woj-server/internal/global"
"github.com/gin-gonic/gin"
@ -23,4 +24,5 @@ func setupApi(g *global.Global, root *gin.RouterGroup) {
var endpoints = []global.EndpointInfo{
{Version: "", Path: "/debug", Register: debug.RouteRegister},
{Version: "/v1", Path: "/user", Register: user.RouteRegister},
{Version: "/v1", Path: "/problem", Register: problem.RouteRegister},
}

View File

@ -4,6 +4,7 @@ import (
"errors"
"github.com/WHUPRJ/woj-server/internal/e"
"github.com/WHUPRJ/woj-server/internal/repo/model"
"github.com/WHUPRJ/woj-server/pkg/utils"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
@ -19,5 +20,5 @@ func (s *service) Query(problemId uint) (*model.Problem, e.Status) {
return nil, e.DatabaseError
}
return problem, e.Success
return problem, utils.If(problem.IsEnabled, e.Success, e.ProblemNotAvailable).(e.Status)
}

View File

@ -5,14 +5,16 @@ import (
"github.com/WHUPRJ/woj-server/internal/e"
"github.com/WHUPRJ/woj-server/internal/repo/model"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
func (s *service) QueryFuzz(search string) ([]*model.Problem, e.Status) {
var problems []*model.Problem
err := s.db.
Where("title LIKE ?", "%"+search+"%").
Or("content LIKE ?", "%"+search+"%").
err := s.db.Preload(clause.Associations).
Where("is_enabled = true").
Where(s.db.Where("title LIKE ?", "%"+search+"%").
Or("content LIKE ?", "%"+search+"%")).
Find(&problems).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, e.ProblemNotFound

View File

@ -12,6 +12,7 @@ var _ Service = (*service)(nil)
type Service interface {
Create(uint, *model.Problem) (*model.Problem, e.Status)
Update(uint, *model.Problem) (*model.Problem, e.Status)
Query(uint) (*model.Problem, e.Status)
QueryFuzz(string) ([]*model.Problem, e.Status)
}
@ -21,7 +22,7 @@ type service struct {
db *gorm.DB
}
func NewProblemService(g *global.Global) Service {
func NewService(g *global.Global) Service {
return &service{
log: g.Log,
db: g.Db.Get().(*gorm.DB),

View File

@ -0,0 +1,20 @@
package problem
import (
"github.com/WHUPRJ/woj-server/internal/e"
"github.com/WHUPRJ/woj-server/internal/repo/model"
"go.uber.org/zap"
"gorm.io/gorm/clause"
)
func (s *service) Update(pid uint, problem *model.Problem) (*model.Problem, e.Status) {
if err := s.db.Clauses(clause.Returning{}).Model(problem).
Where("ID = (?)", pid).
Select("Title", "Content", "TimeLimit", "MemoryLimit", "IsEnabled").
Updates(problem).Error; err != nil {
s.log.Debug("update problem error", zap.Error(err), zap.Any("problem", problem))
return nil, e.DatabaseError
}
return problem, e.Success
}

View File

@ -24,7 +24,7 @@ type service struct {
redis *redis.Client
}
func NewUserService(g *global.Global) Service {
func NewService(g *global.Global) Service {
return &service{
log: g.Log,
db: g.Db.Get().(*gorm.DB),