diff --git a/handler/article.go b/handler/article.go index f61890e..c7a67a2 100644 --- a/handler/article.go +++ b/handler/article.go @@ -1,20 +1,60 @@ package handler import ( - "fmt" - "nCovTrack-Backend/global" - "nCovTrack-Backend/models" - "nCovTrack-Backend/utils" - "github.com/gin-gonic/gin" - "github.com/golang-jwt/jwt/v4" + "nCovTrack-Backend/models" + "nCovTrack-Backend/service/article" + "nCovTrack-Backend/utils" + "strconv" ) func SaveArticleHandler(c *gin.Context) { var articleSave models.BackArticle - c.ShouldBindJSON(&articleSave) - fmt.Println(utils.RenewToken("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NDMxMDA5MDAsImlhdCI6MTY0MzQyNDkwMH0.L8qNmbHJtV8fiKKxGbkZk3DrKBPdvhie_oFooH5hGOY")) - utils.Succ(c, map[string]string{"string": utils.GenerateToken(jwt.MapClaims{})}) - global.Db.First(&articleSave) - fmt.Println(articleSave) + err := c.ShouldBindJSON(&articleSave) + if err != nil { + var requestBody []byte + _, err := c.Request.Body.Read(requestBody) + if err != nil { + return + } + utils.RequestErr(c, requestBody) + return + } + if ok := article.SaveArticle(&articleSave); !ok { + utils.ServerErr(c, "Save Failed") + return + } + utils.Succ(c, articleSave) +} + +func GetAllArticlesHandler(c *gin.Context) { + articles := article.GetArticleList() + utils.Succ(c, articles) +} + +func DeleteArticleHandler(c *gin.Context) { + id, err := strconv.Atoi(c.Param("id")) + if err != nil { + utils.RequestErr(c, map[string]interface{}{"URI": c.Request.RequestURI}) + return + } + if ok := article.DeleteArticle(id); !ok { + utils.DataNotFound(c, "The article not found id = "+strconv.Itoa(id)) + return + } + utils.Succ(c, nil) +} + +func GetArticleHandler(c *gin.Context) { + id, err := strconv.Atoi(c.Param("id")) + if err != nil { + utils.RequestErr(c, map[string]interface{}{"URI": c.Request.RequestURI}) + return + } + res := article.GetArticleById(id) + if res == nil { + utils.DataNotFound(c, nil) + return + } + utils.Succ(c, res) } diff --git a/handler/statistics.go b/handler/statistics.go index f39768f..a778920 100644 --- a/handler/statistics.go +++ b/handler/statistics.go @@ -8,22 +8,22 @@ import ( "github.com/gin-gonic/gin" ) -// Get provience statistics +// ProvinceDataHandler Get province statistics // @Tags Statistics -// @Prodeuce json -// @Summary provience statistics +// @Produce json +// @Summary province statistics // @Success 200 {object} models.GinResponse{data=[]models.AreaInfo} -// @Router /statistics/provience/{sort} [get] +// @Router /statistics/province/{sort} [get] // @Param sort path string false "data sorted by" Enums(today, total, now, default) -func ProvienceDataHandler(c *gin.Context) { +func ProvinceDataHandler(c *gin.Context) { sort := c.Param("sort") data := service.GetAllProvienceData(sort) utils.Succ(c, data) } -// Get city statistics +// CityDataHandler Get city statistics // @Tags Statistics -// @Prodeuce json +// @Produce json // @Summary city statistics // @Success 200 {object} models.GinResponse{data=[]models.AreaInfo} // @Router /statistics/city/{sort} [get] @@ -34,9 +34,9 @@ func CityDataHandler(c *gin.Context) { utils.Succ(c, data) } -// Get country statistics(only china currently) +// CountryDataHandler Get country statistics(only china currently) // @Tags Statistics -// @Prodeuce json +// @Produce json // @Summary country statistics // @Success 200 {object} models.GinResponse{data=[]models.AreaInfo} // @Router /statistics/country/child [get] @@ -47,9 +47,9 @@ func CountryDataHandler(c *gin.Context) { utils.Succ(c, data) } -// Get china data +// ChinaDataHandler Get china data // @Tags Statistics -// @Prodeuce json +// @Produce json // @Summary china data // @Success 200 {object} models.GinResponse{data=models.ChinaData} // @Router /statistics/china [get] diff --git a/models/article.go b/models/article.go index 77ddab6..31e36cf 100644 --- a/models/article.go +++ b/models/article.go @@ -3,7 +3,7 @@ package models import "time" type BackArticle struct { - ID int `gorm:"primaryKey;column:id" json:"-"` // 文章id + ID int `gorm:"primaryKey;column:id" json:"id"` // 文章id CreateTime time.Time `gorm:"column:create_time" json:"createTime"` // 文章新建时间 CreateUser string `gorm:"column:create_user" json:"createUser"` // 文章创建者id ModifyTime time.Time `gorm:"column:modify_time" json:"modifyTime"` // 文章最后更新时间 diff --git a/models/user.go b/models/user.go index b54406d..06720d1 100644 --- a/models/user.go +++ b/models/user.go @@ -7,8 +7,8 @@ type BackUser struct { Username string `gorm:"column:username" json:"username"` // 用户真实姓名 Password string `gorm:"column:password" json:"password"` // 用户密码 Role int `gorm:"column:role" json:"role"` // 用户角色 - Email string `gorm:"column:email" json:"email"` // 用户邮箱 - Phone string `gorm:"column:phone" json:"phone"` // 用户手机号码 + Email string `gorm:"unique;column:email" json:"email"` // 用户邮箱 + Phone string `gorm:"unique;column:phone" json:"phone"` // 用户手机号码 Aptitude string `gorm:"column:aptitude" json:"aptitude"` // 用户资质证明(图片URL) RegisterTime time.Time `gorm:"column:register_time" json:"registerTime"` // 用户注册时间 Approver int `gorm:"column:approver" json:"approver"` // 注册审核人ID diff --git a/note.md b/note.md new file mode 100644 index 0000000..da7239b --- /dev/null +++ b/note.md @@ -0,0 +1,5 @@ +# Notes + +## Base + +- 数据库中的 `user id` 为 `0` 统一代表由系统创建,不为零则是对应的用户创建。 \ No newline at end of file diff --git a/router/article.go b/router/article.go index 1cada66..2ab0847 100644 --- a/router/article.go +++ b/router/article.go @@ -8,6 +8,15 @@ import ( func articlePrivateRouter(router *gin.RouterGroup) { articleRouter := router.Group("/article") { - articleRouter.POST("/save", handler.SaveArticleHandler) + articleRouter.POST("/:id", handler.SaveArticleHandler) + } +} + +func articlePublicRouter(router *gin.RouterGroup) { + articleRouter := router.Group("/article") + { + articleRouter.GET("/list", handler.GetAllArticlesHandler) + articleRouter.DELETE("/:id", handler.DeleteArticleHandler) + articleRouter.GET("/:id", handler.GetArticleHandler) } } diff --git a/router/router.go b/router/router.go index e8e05d8..02f6e2d 100644 --- a/router/router.go +++ b/router/router.go @@ -13,6 +13,7 @@ func BusiRouter() { // Public { statisticRouter(publicRouter) + articlePublicRouter(publicRouter) } // Private diff --git a/router/statistics.go b/router/statistics.go index b4a73a1..263ab83 100644 --- a/router/statistics.go +++ b/router/statistics.go @@ -8,7 +8,7 @@ import ( func statisticRouter(router *gin.RouterGroup) { statisticsRouter := router.Group("/statistics") { - statisticsRouter.GET("/provience/:sort", handler.ProvienceDataHandler) + statisticsRouter.GET("/provience/:sort", handler.ProvinceDataHandler) statisticsRouter.GET("/city/:sort", handler.CityDataHandler) statisticsRouter.GET("/country/child", handler.CountryDataHandler) statisticsRouter.GET("/country", handler.CountryDataHandler) diff --git a/service/article/article.go b/service/article/article.go index 166925c..33674d7 100644 --- a/service/article/article.go +++ b/service/article/article.go @@ -3,15 +3,35 @@ package article import ( "nCovTrack-Backend/global" "nCovTrack-Backend/models" + "nCovTrack-Backend/utils" ) -func GetArticleList() []models.Article { - return make([]models.Article, 0) +func GetArticleList() *[]models.BackArticle { + var articles []models.BackArticle + global.Db.Omit("content").Find(&articles) + return &articles } -func SaveArticle(article models.Article) { - // 前端校验 - // articlea := &models.Article{Content: "#Ceshi", Cover: "https://www.baidu.com/link?url=AWfrkr2rXUGVmKuD08cYx7GwAfQw7qXy_ZczQuH9N_raTP0_eRTv4eZgsdYhtMhS8F7nVl9WfdF01byCD5DAKK&wd=&eqid=b50bb6b100004750000000056202107a", Resume: "sss", Tags: "s", Title: "title", CreateUser: "1", ModifyUser: "1", CreateTime: time.Now(), ModifyTime: time.Now()} - tx := global.Db.Create(article) - print(tx) +func SaveArticle(article *models.BackArticle) (ok bool) { + return utils.Upsert(article) +} + +func DeleteArticle(id int) (ok bool) { + tx := global.Db.Delete(&models.BackArticle{}, id) + if tx.Error != nil { + panic(tx.Error) + } + if tx.RowsAffected == 0 { + return false + } + return false +} + +func GetArticleById(id int) *models.BackArticle { + var article models.BackArticle + tx := global.Db.Limit(1).Find(&article, id) + if tx.RowsAffected == 0 { + return nil + } + return &article } diff --git a/utils/orm.go b/utils/orm.go new file mode 100644 index 0000000..578b66a --- /dev/null +++ b/utils/orm.go @@ -0,0 +1,67 @@ +package utils + +import ( + "gorm.io/gorm/clause" + "nCovTrack-Backend/global" + "reflect" + "regexp" + "time" +) + +var colNameReg, _ = regexp.Compile(".*column:(.*);?") +var uniqueKeyReg, _ = regexp.Compile(".*(primaryKey|unique).*") + +func getNotZeroFields[T any](model T) []string { + t := reflect.TypeOf(model) + v := reflect.ValueOf(model) + var notZeroFields []string + for i := 0; i < t.NumField(); i++ { + if !v.Field(i).IsZero() { + colName := colNameReg.FindSubmatch([]byte(t.Field(i).Tag.Get("gorm"))) + if len(colName) != 2 { + panic("Model Tag regex error") + } + notZeroFields = append(notZeroFields, string(colName[1])) + } + } + return notZeroFields +} + +func Upsert[T any](model *T, forceUpdateFiled ...string) (ok bool) { + t := reflect.TypeOf(model).Elem() + v := reflect.ValueOf(model).Elem() + var uniqueKeyField []clause.Column + notZeroField := NewSet(forceUpdateFiled...).Add("modify_time") + for i := 0; i < t.NumField(); i++ { + gormTag := t.Field(i).Tag.Get("gorm") + if uniqueKey(gormTag) { + uniqueKeyField = append(uniqueKeyField, clause.Column{Name: columnName(gormTag)}) + continue + } + if !v.Field(i).IsZero() { + notZeroField.Add(columnName(gormTag)) + } else if t.Field(i).Type.Name() == "Time" { + v.Field(i).Set(reflect.ValueOf(time.Now())) + } + } + tx := global.Db.Clauses(clause.OnConflict{ + Columns: uniqueKeyField, + DoUpdates: clause.AssignmentColumns(notZeroField.ToSlice()), + }, clause.Returning{}).Create(model) + if tx.Error != nil { + return false + } + return true +} + +func columnName(gormTag string) string { + colNames := colNameReg.FindSubmatch([]byte(gormTag)) + if len(colNames) != 2 { + panic("Model tag regex error") + } + return string(colNames[1]) +} + +func uniqueKey(gormTag string) bool { + return uniqueKeyReg.Match([]byte(gormTag)) +} diff --git a/utils/reflect.go b/utils/reflect.go new file mode 100644 index 0000000..d4b585b --- /dev/null +++ b/utils/reflect.go @@ -0,0 +1 @@ +package utils diff --git a/utils/response.go b/utils/response.go index d92f2b8..fe70572 100644 --- a/utils/response.go +++ b/utils/response.go @@ -7,7 +7,14 @@ import ( "github.com/gin-gonic/gin" ) -func Success(c *gin.Context, code int, msg interface{}, data interface{}) { +const ( + SUCCESS = "Success" + BAD_REQUEST = "Bad Request" + DATA_NOT_FOUND = "Data not Found" + STATUS_DATA_NOT_FOUND = 210 +) + +func Success(c *gin.Context, status int, code int, msg interface{}, data interface{}) { c.JSON(http.StatusOK, models.GinResponse{Code: code, Msg: msg, Data: data}) } @@ -16,9 +23,29 @@ func Error(c *gin.Context, status int, code int, msg interface{}, data interface } func Succ(c *gin.Context, data interface{}) { - Success(c, http.StatusOK, "success", data) + Success(c, http.StatusOK, http.StatusOK, SUCCESS, data) +} + +func DataNotFound(c *gin.Context, data interface{}) { + Success(c, http.StatusOK, STATUS_DATA_NOT_FOUND, DATA_NOT_FOUND, data) } func Err(c *gin.Context, status int, code int, msg interface{}) { - c.JSON(status, models.GinResponse{Code: code, Msg: msg}) + Error(c, status, code, msg, nil) +} + +func ServerError(c *gin.Context, code int, msg interface{}) { + Err(c, http.StatusInternalServerError, code, msg) +} + +func ServerErr(c *gin.Context, msg interface{}) { + ServerError(c, http.StatusInternalServerError, msg) +} + +func RequestError(c *gin.Context, code int, data interface{}) { + Error(c, http.StatusBadRequest, code, BAD_REQUEST, data) +} + +func RequestErr(c *gin.Context, data interface{}) { + RequestError(c, http.StatusBadRequest, data) } diff --git a/utils/set.go b/utils/set.go new file mode 100644 index 0000000..e21b95a --- /dev/null +++ b/utils/set.go @@ -0,0 +1,38 @@ +package utils + +type void struct{} + +type Set[T comparable] struct { + setMap map[T]void +} + +func NewSet[T comparable](eles ...T) *Set[T] { + set := &Set[T]{setMap: make(map[T]void)} + set.AddAll(eles...) + return set +} + +func (set *Set[T]) Add(ele T) *Set[T] { + set.setMap[ele] = void{} + return set +} + +func (set *Set[T]) AddAll(eles ...T) *Set[T] { + for _, ele := range eles { + set.Add(ele) + } + return set +} + +func (set *Set[T]) Delete(ele T) *Set[T] { + delete(set.setMap, ele) + return set +} + +func (set *Set[T]) ToSlice() []T { + var s []T + for k, _ := range set.setMap { + s = append(s, k) + } + return s +}