فهرست منبع

feat: 创建话题接口

runningwater 6 ماه پیش
والد
کامیت
f5f8af3939

+ 40 - 0
app/http/controllers/api/v1/topics_controller.go

@@ -0,0 +1,40 @@
+package v1
+
+import (
+	"github.com/runningwater/gohub/app/models/topic"
+	"github.com/runningwater/gohub/app/requests"
+	"github.com/runningwater/gohub/pkg/auth"
+	"github.com/runningwater/gohub/pkg/response"
+
+	"github.com/gin-gonic/gin"
+)
+
+type TopicsController struct {
+	BaseApiController
+}
+
+// func (ctrl *TopicsController) Index(c *gin.Context) {
+//     topics := topic.All()
+//     response.Data(c, topics)
+// }
+
+func (ctrl *TopicsController) Store(c *gin.Context) {
+
+	request := requests.TopicRequest{}
+	if ok := requests.Validate(c, &request, requests.TopicSave); !ok {
+		return
+	}
+
+	topicModel := topic.Topic{
+		Title:      request.Title,
+		Body:       request.Body,
+		CategoryID: request.CategoryID,
+		UserID:     auth.CurrentUID(c),
+	}
+	topicModel.Create()
+	if topicModel.ID > 0 {
+		response.Created(c, topicModel)
+	} else {
+		response.Abort500(c, "创建失败,请稍后尝试~")
+	}
+}

+ 27 - 0
app/requests/topic_request.go

@@ -0,0 +1,27 @@
+package requests
+
+import (
+	"github.com/gin-gonic/gin"
+	"github.com/thedevsaddam/govalidator"
+)
+
+type TopicRequest struct {
+	Title      string `json:"title,omitempty" valid:"title"`
+	Body       string `json:"body,omitempty" valid:"body"`
+	CategoryID string `json:"category_id,omitempty" valid:"category_id"`
+}
+
+func TopicSave(data any, c *gin.Context) map[string][]string {
+
+	rules := govalidator.MapData{
+		"title":       []string{"required", "min_cn:3", "max_cn:40"},
+		"body":        []string{"required", "min_cn:10", "max_cn:50000"},
+		"category_id": []string{"required", "exists:categories,id"},
+	}
+	messages := govalidator.MapData{
+		"title":       []string{"required:标题为必填项", "min_cn:标题长度需至少 3 个字符", "max_cn:标题长度不能超过 40 个字符"},
+		"body":        []string{"required:内容为必填项", "min_cn:内容长度需至少 10 个字符", "max_cn:内容长度不能超过 50000 个字符"},
+		"category_id": []string{"required:分类为必填项", "exists:分类不存在"},
+	}
+	return validate(data, rules, messages)
+}

+ 29 - 0
app/requests/validators/custom_rules.go

@@ -87,4 +87,33 @@ func init() {
 		}
 		return nil
 	})
+
+	// 自定义规则 exists, 确保数据库存在某条数据
+	// exists:categories,id
+	govalidator.AddCustomRule("exists", func(field string, rule string, message string, value any) error {
+		rng := strings.Split(strings.TrimPrefix(rule, "exists:"), ",")
+
+		// 第一个参数,表名称,如 categories
+		tableName := rng[0]
+		// 第二个参数,字段名称,如 id
+		fieldName := rng[1]
+
+		// 用户请求的值
+		requestValue := value.(string)
+
+		// 调用数据库查询方法,检查数据是否存在
+		var count int64
+		database.DB.Table(tableName).Where(fieldName+" = ?", requestValue).Count(&count)
+
+		// 如果 count 小于等于 0,表示数据不存在,返回错误信息
+		if count <= 0 {
+			// 如果有自定义错误消息的话,使用自定义消息
+			if message != "" {
+				return errors.New(message)
+			}
+			return fmt.Errorf("%v 不存在", requestValue)
+		}
+
+		return nil
+	})
 }

+ 12 - 1
gohub.http

@@ -123,4 +123,15 @@ Content-Type: application/json
 ### 删除分类
 DELETE {{base_url}}/v1/categories/1 HTTP/1.1
 Authorization: Bearer {{access_token}}
-Content-Type: application/json
+Content-Type: application/json
+
+### 创建话题
+POST {{base_url}}/v1/topics HTTP/1.1
+Authorization: Bearer {{access_token}}
+Content-Type: application/json
+
+{
+  "title": "我的的帖子",
+  "body": "话题1内容, 这里是帖子描述内容帖子描述内容",
+  "category_id": "2"
+}

+ 7 - 6
pkg/logger/logger.go

@@ -8,10 +8,11 @@ import (
 	"strings"
 	"time"
 
-	"github.com/runningwater/gohub/pkg/app"
 	"go.uber.org/zap"
 	"go.uber.org/zap/zapcore"
 	"gopkg.in/natefinch/lumberjack.v2"
+
+	"github.com/runningwater/gohub/pkg/app"
 )
 
 // Logger 全局日志对象
@@ -33,7 +34,7 @@ func Init(filename string, maxSize, maxBackup, maxAge int, compress bool, logTyp
 	Logger = zap.New(core,
 		zap.AddCaller(),      // 调用文件和行号,内部使用 runtime.Caller
 		zap.AddCallerSkip(1), // 调用文件和行号,内部使用 runtime.Caller
-		//zap.Development(), // 开发模式,堆栈跟踪
+		// zap.Development(), // 开发模式,堆栈跟踪
 		zap.AddStacktrace(zapcore.ErrorLevel), // 记录错误级别以上的堆栈信息
 	)
 
@@ -80,8 +81,8 @@ func customTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
 func getLogWriter(filename string, maxSize, maxBackup, maxAge int, compress bool, logType string) zapcore.WriteSyncer {
 	// 如果配置了按日期记录日志,则使用按日期记录日志
 	if logType == "daily" {
-		logname := time.Now().Format("2006-01-02") + ".log"
-		filename = strings.Replace(filename, "logs.log", logname, 1)
+		logName := time.Now().Format("2006-01-02") + ".log"
+		filename = strings.Replace(filename, "logs.log", logName, 1)
 	}
 
 	// 滚动日志,详见 config/log.go
@@ -169,8 +170,8 @@ func Fatal(msg string, fields ...zap.Field) {
 // DebugString 记录一条字符串类型的 debug 日志
 //
 //	logger.DebugString("User", "name", "John")
-func DebugString(modeleName, name, msg string) {
-	Logger.Debug(modeleName, zap.String(name, msg))
+func DebugString(moduleName, name, msg string) {
+	Logger.Debug(moduleName, zap.String(name, msg))
 }
 
 func InfoString(moduleName, name, msg string) {

+ 4 - 2
pkg/response/response.go

@@ -2,11 +2,13 @@
 package response
 
 import (
+	"errors"
 	"net/http"
 
 	"github.com/gin-gonic/gin"
-	"github.com/runningwater/gohub/pkg/logger"
 	"gorm.io/gorm"
+
+	"github.com/runningwater/gohub/pkg/logger"
 )
 
 // JSON 响应 200 和 JSON 数据
@@ -83,7 +85,7 @@ func Error(c *gin.Context, err error, msg ...string) {
 	logger.LogIf(err)
 
 	// error 类型为『数据库未找到内容』
-	if err == gorm.ErrRecordNotFound {
+	if errors.Is(err, gorm.ErrRecordNotFound) {
 		Abort404(c)
 		return
 	}

+ 5 - 1
routes/api.go

@@ -58,7 +58,6 @@ func RegisterAPIRoutes(router *gin.Engine) {
 		{
 			userGroup.GET("", uc.Index)
 		}
-
 		cgc := new(controllers.CategoriesController)
 		categoryGroup := v1.Group("/categories")
 		{
@@ -67,5 +66,10 @@ func RegisterAPIRoutes(router *gin.Engine) {
 			categoryGroup.PUT("/:id", middlewares.AuthJWT(), cgc.Update)
 			categoryGroup.DELETE("/:id", middlewares.AuthJWT(), cgc.Delete)
 		}
+		tpc := new(controllers.TopicsController)
+		tpcGroup := v1.Group("/topics")
+		{
+			tpcGroup.POST("", middlewares.AuthJWT(), tpc.Store)
+		}
 	}
 }