feat: article: finish base functions
This commit is contained in:
@@ -1,20 +1,60 @@
|
|||||||
package handler
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"nCovTrack-Backend/global"
|
|
||||||
"nCovTrack-Backend/models"
|
|
||||||
"nCovTrack-Backend/utils"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"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) {
|
func SaveArticleHandler(c *gin.Context) {
|
||||||
var articleSave models.BackArticle
|
var articleSave models.BackArticle
|
||||||
c.ShouldBindJSON(&articleSave)
|
err := c.ShouldBindJSON(&articleSave)
|
||||||
fmt.Println(utils.RenewToken("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NDMxMDA5MDAsImlhdCI6MTY0MzQyNDkwMH0.L8qNmbHJtV8fiKKxGbkZk3DrKBPdvhie_oFooH5hGOY"))
|
if err != nil {
|
||||||
utils.Succ(c, map[string]string{"string": utils.GenerateToken(jwt.MapClaims{})})
|
var requestBody []byte
|
||||||
global.Db.First(&articleSave)
|
_, err := c.Request.Body.Read(requestBody)
|
||||||
fmt.Println(articleSave)
|
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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,22 +8,22 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Get provience statistics
|
// ProvinceDataHandler Get province statistics
|
||||||
// @Tags Statistics
|
// @Tags Statistics
|
||||||
// @Prodeuce json
|
// @Produce json
|
||||||
// @Summary provience statistics
|
// @Summary province statistics
|
||||||
// @Success 200 {object} models.GinResponse{data=[]models.AreaInfo}
|
// @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)
|
// @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")
|
sort := c.Param("sort")
|
||||||
data := service.GetAllProvienceData(sort)
|
data := service.GetAllProvienceData(sort)
|
||||||
utils.Succ(c, data)
|
utils.Succ(c, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get city statistics
|
// CityDataHandler Get city statistics
|
||||||
// @Tags Statistics
|
// @Tags Statistics
|
||||||
// @Prodeuce json
|
// @Produce json
|
||||||
// @Summary city statistics
|
// @Summary city statistics
|
||||||
// @Success 200 {object} models.GinResponse{data=[]models.AreaInfo}
|
// @Success 200 {object} models.GinResponse{data=[]models.AreaInfo}
|
||||||
// @Router /statistics/city/{sort} [get]
|
// @Router /statistics/city/{sort} [get]
|
||||||
@@ -34,9 +34,9 @@ func CityDataHandler(c *gin.Context) {
|
|||||||
utils.Succ(c, data)
|
utils.Succ(c, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get country statistics(only china currently)
|
// CountryDataHandler Get country statistics(only china currently)
|
||||||
// @Tags Statistics
|
// @Tags Statistics
|
||||||
// @Prodeuce json
|
// @Produce json
|
||||||
// @Summary country statistics
|
// @Summary country statistics
|
||||||
// @Success 200 {object} models.GinResponse{data=[]models.AreaInfo}
|
// @Success 200 {object} models.GinResponse{data=[]models.AreaInfo}
|
||||||
// @Router /statistics/country/child [get]
|
// @Router /statistics/country/child [get]
|
||||||
@@ -47,9 +47,9 @@ func CountryDataHandler(c *gin.Context) {
|
|||||||
utils.Succ(c, data)
|
utils.Succ(c, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get china data
|
// ChinaDataHandler Get china data
|
||||||
// @Tags Statistics
|
// @Tags Statistics
|
||||||
// @Prodeuce json
|
// @Produce json
|
||||||
// @Summary china data
|
// @Summary china data
|
||||||
// @Success 200 {object} models.GinResponse{data=models.ChinaData}
|
// @Success 200 {object} models.GinResponse{data=models.ChinaData}
|
||||||
// @Router /statistics/china [get]
|
// @Router /statistics/china [get]
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package models
|
|||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
type BackArticle struct {
|
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"` // 文章新建时间
|
CreateTime time.Time `gorm:"column:create_time" json:"createTime"` // 文章新建时间
|
||||||
CreateUser string `gorm:"column:create_user" json:"createUser"` // 文章创建者id
|
CreateUser string `gorm:"column:create_user" json:"createUser"` // 文章创建者id
|
||||||
ModifyTime time.Time `gorm:"column:modify_time" json:"modifyTime"` // 文章最后更新时间
|
ModifyTime time.Time `gorm:"column:modify_time" json:"modifyTime"` // 文章最后更新时间
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ type BackUser struct {
|
|||||||
Username string `gorm:"column:username" json:"username"` // 用户真实姓名
|
Username string `gorm:"column:username" json:"username"` // 用户真实姓名
|
||||||
Password string `gorm:"column:password" json:"password"` // 用户密码
|
Password string `gorm:"column:password" json:"password"` // 用户密码
|
||||||
Role int `gorm:"column:role" json:"role"` // 用户角色
|
Role int `gorm:"column:role" json:"role"` // 用户角色
|
||||||
Email string `gorm:"column:email" json:"email"` // 用户邮箱
|
Email string `gorm:"unique;column:email" json:"email"` // 用户邮箱
|
||||||
Phone string `gorm:"column:phone" json:"phone"` // 用户手机号码
|
Phone string `gorm:"unique;column:phone" json:"phone"` // 用户手机号码
|
||||||
Aptitude string `gorm:"column:aptitude" json:"aptitude"` // 用户资质证明(图片URL)
|
Aptitude string `gorm:"column:aptitude" json:"aptitude"` // 用户资质证明(图片URL)
|
||||||
RegisterTime time.Time `gorm:"column:register_time" json:"registerTime"` // 用户注册时间
|
RegisterTime time.Time `gorm:"column:register_time" json:"registerTime"` // 用户注册时间
|
||||||
Approver int `gorm:"column:approver" json:"approver"` // 注册审核人ID
|
Approver int `gorm:"column:approver" json:"approver"` // 注册审核人ID
|
||||||
|
|||||||
5
note.md
Normal file
5
note.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Notes
|
||||||
|
|
||||||
|
## Base
|
||||||
|
|
||||||
|
- 数据库中的 `user id` 为 `0` 统一代表由系统创建,不为零则是对应的用户创建。
|
||||||
@@ -8,6 +8,15 @@ import (
|
|||||||
func articlePrivateRouter(router *gin.RouterGroup) {
|
func articlePrivateRouter(router *gin.RouterGroup) {
|
||||||
articleRouter := router.Group("/article")
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ func BusiRouter() {
|
|||||||
// Public
|
// Public
|
||||||
{
|
{
|
||||||
statisticRouter(publicRouter)
|
statisticRouter(publicRouter)
|
||||||
|
articlePublicRouter(publicRouter)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Private
|
// Private
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
func statisticRouter(router *gin.RouterGroup) {
|
func statisticRouter(router *gin.RouterGroup) {
|
||||||
statisticsRouter := router.Group("/statistics")
|
statisticsRouter := router.Group("/statistics")
|
||||||
{
|
{
|
||||||
statisticsRouter.GET("/provience/:sort", handler.ProvienceDataHandler)
|
statisticsRouter.GET("/provience/:sort", handler.ProvinceDataHandler)
|
||||||
statisticsRouter.GET("/city/:sort", handler.CityDataHandler)
|
statisticsRouter.GET("/city/:sort", handler.CityDataHandler)
|
||||||
statisticsRouter.GET("/country/child", handler.CountryDataHandler)
|
statisticsRouter.GET("/country/child", handler.CountryDataHandler)
|
||||||
statisticsRouter.GET("/country", handler.CountryDataHandler)
|
statisticsRouter.GET("/country", handler.CountryDataHandler)
|
||||||
|
|||||||
@@ -3,15 +3,35 @@ package article
|
|||||||
import (
|
import (
|
||||||
"nCovTrack-Backend/global"
|
"nCovTrack-Backend/global"
|
||||||
"nCovTrack-Backend/models"
|
"nCovTrack-Backend/models"
|
||||||
|
"nCovTrack-Backend/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetArticleList() []models.Article {
|
func GetArticleList() *[]models.BackArticle {
|
||||||
return make([]models.Article, 0)
|
var articles []models.BackArticle
|
||||||
|
global.Db.Omit("content").Find(&articles)
|
||||||
|
return &articles
|
||||||
}
|
}
|
||||||
|
|
||||||
func SaveArticle(article models.Article) {
|
func SaveArticle(article *models.BackArticle) (ok bool) {
|
||||||
// 前端校验
|
return utils.Upsert(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 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
|
||||||
}
|
}
|
||||||
|
|||||||
67
utils/orm.go
Normal file
67
utils/orm.go
Normal file
@@ -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))
|
||||||
|
}
|
||||||
1
utils/reflect.go
Normal file
1
utils/reflect.go
Normal file
@@ -0,0 +1 @@
|
|||||||
|
package utils
|
||||||
@@ -7,7 +7,14 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"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})
|
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{}) {
|
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{}) {
|
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)
|
||||||
}
|
}
|
||||||
|
|||||||
38
utils/set.go
Normal file
38
utils/set.go
Normal file
@@ -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
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user