完成基础model部分

This commit is contained in:
loser 2022-05-25 17:30:46 +08:00
parent ddec628641
commit 9bb43f4484
18 changed files with 498 additions and 0 deletions

8
.idea/modules.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/online_code.iml" filepath="$PROJECT_DIR$/.idea/online_code.iml" />
</modules>
</component>
</project>

9
.idea/online_code.iml Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="Go" enabled="true" />
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View File

@ -0,0 +1,6 @@
package constant
const (
OAuth2RedisKey = "oauth:token:" // Token缓存前缀
OAuth2UserCacheKey = "oauth:user:" // 用户缓存前缀
)

38
common/constant/user.go Normal file
View File

@ -0,0 +1,38 @@
package constant
// UserStatus 用户状态
type UserStatus string
const (
UserStatusActive UserStatus = "NORMAL" // 用户状态正常
UserStatusDisabled UserStatus = "DISABLE" // 已禁用用户
)
// 状态对应的描述
var userStatusMap = map[UserStatus]string{
UserStatusActive: "正常",
UserStatusDisabled: "已禁用",
}
// 处理为看得懂的状态
func (s UserStatus) String() string {
if str, ok := userStatusMap[s]; ok {
return str
}
return string(s)
}
// =====================================================================================================================
// UserIdentity 用户身份
type UserIdentity string
const (
UserIdentityAdmin UserIdentity = "admin" // 管理员
UserIdentityUser UserIdentity = "user" // 普通用户
)
// String implements the Stringer interface.
func (t UserIdentity) String() string {
return string(t)
}

6
common/default_keys.go Normal file
View File

@ -0,0 +1,6 @@
package common
const (
SmsSendKey = "sms:send:" // 短信发送缓存前缀
Oauth2RedisKey = "oauth:token:" // Token缓存前缀
)

105
common/types/date.go Normal file
View File

@ -0,0 +1,105 @@
package types
import (
"database/sql/driver"
"fmt"
"time"
)
// 默认时间格式
const dateFormat = "2006-01-02 15:04:05.000"
// DateTime 自定义时间类型
type DateTime time.Time
// Scan implements the Scanner interface.
func (dt *DateTime) Scan(value interface{}) error {
// mysql 内部日期的格式可能是 2006-01-02 15:04:05 +0800 CST 格式,所以检出的时候还需要进行一次格式化
tTime, _ := time.Parse("2006-01-02 15:04:05 +0800 CST", value.(time.Time).String())
*dt = DateTime(tTime)
return nil
}
// Value implements the driver Valuer interface.
func (dt DateTime) Value() (driver.Value, error) {
// 0001-01-01 00:00:00 属于空值,遇到空值解析成 null 即可
if dt.String() == "0001-01-01 00:00:00.000" {
return nil, nil
}
return []byte(dt.Format(dateFormat)), nil
}
// 用于 fmt.Println 和后续验证场景
func (dt DateTime) String() string {
return dt.Format(dateFormat)
}
// Format 格式化
func (dt DateTime) Format(fm string) string {
return time.Time(dt).Format(fm)
}
// After 时间比较
func (dt *DateTime) After(now time.Time) bool {
return time.Time(*dt).After(now)
}
// Before 时间比较
func (dt *DateTime) Before(now time.Time) bool {
return time.Time(*dt).Before(now)
}
// IBefore 时间比较
func (dt *DateTime) IBefore(now DateTime) bool {
return dt.Before(time.Time(now))
}
// SubTime 对比
func (dt DateTime) SubTime(t time.Time) time.Duration {
return dt.ToTime().Sub(t)
}
// Sub 对比
func (dt DateTime) Sub(t DateTime) time.Duration {
return dt.ToTime().Sub(t.ToTime())
}
// ToTime 转换为golang的时间类型
func (dt DateTime) ToTime() time.Time {
return time.Time(dt)
}
// IsNil 是否为空值
func (dt DateTime) IsNil() bool {
return dt.Format(dateFormat) == "0001-01-01 00:00:00.000"
}
// Unix 实现Unix函数
func (dt DateTime) Unix() int64 {
return dt.ToTime().Unix()
}
// ======== 序列化 ========
// MarshalJSON 时间到字符串
func (dt DateTime) MarshalJSON() ([]byte, error) {
// 过滤掉空数据
if dt.IsNil() {
return []byte("\"\""), nil
}
output := fmt.Sprintf(`"%s"`, dt.Format("2006-01-02 15:04:05"))
return []byte(output), nil
}
// UnmarshalJSON 字符串到时间
func (dt *DateTime) UnmarshalJSON(b []byte) error {
if len(b) == 2 {
*dt = DateTime{}
return nil
}
// 解析指定的格式
//now, err := time.ParseInLocation(`"`+dateFormat+`"`, string(b), time.Local)
now, err := time.ParseInLocation(dateFormat, string(b), time.Local)
*dt = DateTime(now)
return err
}

20
common/types/model.go Normal file
View File

@ -0,0 +1,20 @@
package types
import (
"github.com/google/uuid"
"gorm.io/gorm"
)
// BaseDbModel 数据库通用字段
type BaseDbModel struct {
Id string `json:"id" gorm:"type:varchar(50);primarykey"`
CreatedAt DateTime `json:"createdAt"`
UpdatedAt DateTime `json:"updatedAt"`
DeletedAt gorm.DeletedAt `json:"-" gorm:"index:deleted"`
}
// BeforeCreate 创建数据库对象之前生成UUID
func (m *BaseDbModel) BeforeCreate(*gorm.DB) (err error) {
m.Id = uuid.New().String()
return
}

52
core/error_handle.go Normal file
View File

@ -0,0 +1,52 @@
package core
import (
"fmt"
"git.echol.cn/loser/logger/log"
"github.com/gin-gonic/gin"
"net"
"net/http"
"os"
"strings"
)
// NoMethodHandler 请求方式不对
func NoMethodHandler() gin.HandlerFunc {
return func(ctx *gin.Context) {
R(ctx).FailWithMessageAndCode(fmt.Sprintf("不支持%v请求", ctx.Request.Method), http.StatusMethodNotAllowed)
ctx.Abort()
}
}
// NoRouteHandler 404异常处理
func NoRouteHandler() gin.HandlerFunc {
return func(ctx *gin.Context) {
R(ctx).FailWithMessageAndCode("请求接口不存在", http.StatusNotFound)
ctx.Abort()
}
}
// Recovery Panic捕获
func Recovery() gin.HandlerFunc {
return func(ctx *gin.Context) {
defer func() {
if err := recover(); err != nil {
log.Errorf("系统错误: %v", err)
var brokenPipe bool
if ne, ok := err.(*net.OpError); ok {
if se, ok := ne.Err.(*os.SyscallError); ok {
if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") {
brokenPipe = true
}
}
}
if brokenPipe {
log.Errorf("%s", err)
}
R(ctx).FailWithMessage("服务器异常,请联系管理员")
ctx.Abort()
}
}()
ctx.Next()
}
}

75
core/response.go Normal file
View File

@ -0,0 +1,75 @@
package core
import (
"github.com/gin-gonic/gin"
"net/http"
)
// 返回数据包装
type response struct {
Code int `json:"code"`
Data any `json:"data"`
Msg string `json:"message"`
}
type rs struct {
ctx *gin.Context
}
// 定义状态码
const (
ERROR = http.StatusInternalServerError
SUCCESS = http.StatusOK
)
func R(ctx *gin.Context) *rs {
return &rs{ctx}
}
// Result 手动组装返回结果
func (r rs) Result(code int, data interface{}, msg string) {
//if data == nil {
// data = map[string]interface{}{}
//}
r.ctx.JSON(code, response{
code,
data,
msg,
})
}
// Ok 返回无数据的成功
func (r rs) Ok() {
r.Result(SUCCESS, nil, "操作成功")
}
// OkWithMessage 返回自定义成功的消息
func (r rs) OkWithMessage(message string) {
r.Result(SUCCESS, nil, message)
}
// OkWithData 自定义内容的成功返回
func (r rs) OkWithData(data interface{}) {
r.Result(SUCCESS, data, "操作成功")
}
// OkDetailed 自定义消息和内容的成功返回
func (r rs) OkDetailed(data interface{}, message string) {
r.Result(SUCCESS, data, message)
}
// Fail 返回默认失败
func (r rs) Fail() {
r.Result(ERROR, nil, "操作失败")
}
// FailWithMessage 返回默认状态码自定义消息的失败
func (r rs) FailWithMessage(message string) {
r.Result(ERROR, nil, message)
}
// FailWithMessageAndCode 返回自定义消息和状态码的失败
func (r rs) FailWithMessageAndCode(message string, code int) {
r.Result(code, nil, message)
}

10
core/response_page.go Normal file
View File

@ -0,0 +1,10 @@
package core
// PageData 分页数据通用结构体
type PageData struct {
Current int `json:"current"` // 当前页码
Size int `json:"size"` // 每页数量
Total int64 `json:"total"` // 总数
TotalPage int `json:"total_page"` // 总页数
Records interface{} `json:"records"` // 返回数据
}

59
go.mod Normal file
View File

@ -0,0 +1,59 @@
module online_code
go 1.18
require (
git.echol.cn/loser/logger v1.0.14
github.com/gin-gonic/gin v1.7.7
github.com/google/uuid v1.1.2
gorm.io/gorm v1.23.5
)
require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/caarlos0/env/v6 v6.9.2 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-kit/kit v0.12.0 // indirect
github.com/go-kit/log v0.2.1 // indirect
github.com/go-logfmt/logfmt v0.5.1 // indirect
github.com/go-playground/locales v0.13.0 // indirect
github.com/go-playground/universal-translator v0.17.0 // indirect
github.com/go-playground/validator/v10 v10.4.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.4 // indirect
github.com/jpillora/backoff v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/leodido/go-urn v1.2.0 // indirect
github.com/lixh00/loki-client-go v1.0.1 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
github.com/natefinch/lumberjack v2.0.0+incompatible // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_golang v1.12.2 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.34.0 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
github.com/prometheus/prometheus v1.8.2-0.20201028100903-3245b3267b24 // indirect
github.com/ugorji/go/codec v1.1.7 // indirect
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
golang.org/x/text v0.3.7 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3 // indirect
google.golang.org/grpc v1.46.2 // indirect
google.golang.org/protobuf v1.28.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)

15
models/entity/category.go Normal file
View File

@ -0,0 +1,15 @@
package entity
import "online_code/common/types"
// Category 分类表
type Category struct {
types.BaseDbModel
Identity string `json:"identity" gorm:"column:identity;type:varchar(36);comment:'分类的唯一标识'"`
Name string `json:"name" gorm:"column:name;type:varchar(100);comment:'分类名称'" `
ParentId int `json:"parent_id" gorm:"column:parent_id;type:int(11);comment:'父级ID'" `
}
func (table *Category) TableName() string {
return "category"
}

21
models/entity/problem.go Normal file
View File

@ -0,0 +1,21 @@
package entity
import "online_code/common/types"
// Problem 问题表
type Problem struct {
types.BaseDbModel
Identity string `json:"identity" gorm:"column:identity;type:varchar(36);comment:'问题表的唯一标识'" ` // 问题表的唯一标识
ProblemCategories []*ProblemCategory `json:"problem_categories" gorm:"foreignKey:problem_id;references:id;comment:'关联问题分类表'" ` // 关联问题分类表
Title string `json:"title" gorm:"column:title;type:varchar(255);comment:'文章标题'" ` // 文章标题
Content string `json:"content" gorm:"column:content;type:text;comment:'文章正文'" ` // 文章正文
MaxRuntime int `json:"max_runtime" gorm:"column:max_runtime;type:int(11);comment:'最大运行时长'" ` // 最大运行时长
MaxMem int `json:"max_mem" gorm:"column:max_mem;type:int(11);comment:'最大运行内存'" ` // 最大运行内存
TestCases []*TestCase `json:"test_cases" gorm:"foreignKey:problem_identity;references:identity;comment:'关联测试用例表'" ` // 关联测试用例表
PassNum int64 `json:"pass_num" gorm:"column:pass_num;type:int(11);comment:'通过次数'" ` // 通过次数
SubmitNum int64 `json:"submit_num" gorm:"column:submit_num;type:int(11);comment:'提交次数'" ` // 提交次数
}
func (table *Problem) TableName() string {
return "problem_basic"
}

View File

@ -0,0 +1,17 @@
package entity
import (
"online_code/common/types"
)
// ProblemCategory 问题分类表
type ProblemCategory struct {
types.BaseDbModel
ProblemId uint `json:"problem_id" gorm:"column:problem_id;type:int(11);comment:'问题的ID'" ` // 问题的ID
CategoryId uint `json:"category_id" gorm:"column:category_id;type:int(11);comment:'分类的ID'" ` // 分类的ID
CategoryBasic *Category `json:"category_basic" gorm:"foreignKey:id;references:category_id;comment:'关联分类的基础信息表'" ` // 关联分类的基础信息表
}
func (table *ProblemCategory) TableName() string {
return "problem_category"
}

16
models/entity/submit.go Normal file
View File

@ -0,0 +1,16 @@
package entity
// Submit 提交表
type Submit struct {
Identity string `json:"identity" gorm:"column:identity;type:varchar(36);comment:'用户唯一标识'" ` // 唯一标识
ProblemIdentity string `json:"problem_identity" gorm:"column:problem_identity;type:varchar(36);comment:'问题表的唯一标识'" ` // 问题表的唯一标识
ProblemBasic *Problem `json:"problem_basic" gorm:"foreignKey:identity;references:problem_identity;comment:'关联问题基础表'" ` // 关联问题基础表
UserIdentity string `json:"user_identity" gorm:"column:user_identity;type:varchar(36);comment:'用户表的唯一标识'" ` // 用户表的唯一标识
UserBasic *User `json:"user_basic" gorm:"foreignKey:identity;references:user_identity;comment:'联用户基础表'" ` // 关联用户基础表
Path string `json:"path" gorm:"column:path;type:varchar(255);comment:'代码存放路径'" ` // 代码存放路径
Status int `json:"status" gorm:"column:status;type:tinyint(1);comment:'状态'" ` // 【-1-待判断1-答案正确2-答案错误3-运行超时4-运行超内存, 5-编译错误】
}
func (table *Submit) TableName() string {
return "submit"
}

View File

@ -0,0 +1,16 @@
package entity
import "online_code/common/types"
// TestCase 测试用例
type TestCase struct {
types.BaseDbModel
Identity string `json:"identity" gorm:"column:identity;type:varchar(255);comment:'用户唯一标识''" `
ProblemIdentity string `json:"problem_identity" gorm:"column:problem_identity;type:varchar(255);comment:'问题'" `
Input string `json:"input" gorm:"column:input;type:text;" `
Output string `json:"output" gorm:"column:output;type:text;" `
}
func (table *TestCase) TableName() string {
return "test_case"
}

19
models/entity/user.go Normal file
View File

@ -0,0 +1,19 @@
package entity
import "online_code/common/types"
type User struct {
types.BaseDbModel
Identity string `json:"identity" gorm:"column:identity;type:varchar(36);comment:'用户唯一标识'"`
Name string `json:"name" gorm:"column:name;type:varchar(100);not null;comment:'用户名'"`
Password string `json:"password" gorm:"column:password;type:varchar(32);not null;comment:'用户密码'"`
Phone string `json:"phone" gorm:"column:phone;type:varchar(20);not null;comment:'用户手机号'"`
Mail string `json:"mail" gorm:"column:mail;type:varchar(100);not null;comment:'用户邮箱'"`
PassNum int64 `json:"pass_num" gorm:"column:pass_num;type:int(11);comment:'通过的次数'"`
SubmitNum int64 `json:"submit_num" gorm:"column:submit_num;type:int(11);comment:'提交次数'"`
IsAdmin int `json:"is_admin" gorm:"column:is_admin;type:tinyint(1);comment:'是否是管理员【0-否1-是】'"`
}
func (table *User) TableName() string {
return "user"
}