diff --git a/docs/docs.go b/docs/docs.go index 700e62b..385a749 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -640,6 +640,64 @@ const docTemplate = `{ } } } + }, + "/api/upload/text": { + "post": { + "description": "中转一段长文本内容并创建一个提取批次", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Public" + ], + "summary": "发送长文本", + "parameters": [ + { + "description": "文本内容及配置", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/public.UploadTextRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/model.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/public.UploadResponse" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/model.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/model.Response" + } + } + } + } } }, "definitions": { @@ -762,6 +820,9 @@ const docTemplate = `{ "model.FileBatch": { "type": "object", "properties": { + "content": { + "type": "string" + }, "created_at": { "type": "string" }, @@ -797,6 +858,10 @@ const docTemplate = `{ "description": "active / expired / deleted", "type": "string" }, + "type": { + "description": "file / text", + "type": "string" + }, "updated_at": { "type": "string" } @@ -845,6 +910,9 @@ const docTemplate = `{ "public.PickupResponse": { "type": "object", "properties": { + "content": { + "type": "string" + }, "download_count": { "type": "integer" }, @@ -865,6 +933,9 @@ const docTemplate = `{ }, "remark": { "type": "string" + }, + "type": { + "type": "string" } } }, @@ -881,6 +952,34 @@ const docTemplate = `{ "type": "string" } } + }, + "public.UploadTextRequest": { + "type": "object", + "required": [ + "content" + ], + "properties": { + "content": { + "type": "string", + "example": "这是一段长文本内容..." + }, + "expire_days": { + "type": "integer", + "example": 7 + }, + "expire_type": { + "type": "string", + "example": "time" + }, + "max_downloads": { + "type": "integer", + "example": 5 + }, + "remark": { + "type": "string", + "example": "文本备注" + } + } } }, "securityDefinitions": { diff --git a/docs/swagger.json b/docs/swagger.json index 15bf873..abc3317 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -633,6 +633,64 @@ } } } + }, + "/api/upload/text": { + "post": { + "description": "中转一段长文本内容并创建一个提取批次", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Public" + ], + "summary": "发送长文本", + "parameters": [ + { + "description": "文本内容及配置", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/public.UploadTextRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/model.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/public.UploadResponse" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/model.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/model.Response" + } + } + } + } } }, "definitions": { @@ -755,6 +813,9 @@ "model.FileBatch": { "type": "object", "properties": { + "content": { + "type": "string" + }, "created_at": { "type": "string" }, @@ -790,6 +851,10 @@ "description": "active / expired / deleted", "type": "string" }, + "type": { + "description": "file / text", + "type": "string" + }, "updated_at": { "type": "string" } @@ -838,6 +903,9 @@ "public.PickupResponse": { "type": "object", "properties": { + "content": { + "type": "string" + }, "download_count": { "type": "integer" }, @@ -858,6 +926,9 @@ }, "remark": { "type": "string" + }, + "type": { + "type": "string" } } }, @@ -874,6 +945,34 @@ "type": "string" } } + }, + "public.UploadTextRequest": { + "type": "object", + "required": [ + "content" + ], + "properties": { + "content": { + "type": "string", + "example": "这是一段长文本内容..." + }, + "expire_days": { + "type": "integer", + "example": 7 + }, + "expire_type": { + "type": "string", + "example": "time" + }, + "max_downloads": { + "type": "integer", + "example": 5 + }, + "remark": { + "type": "string", + "example": "文本备注" + } + } } }, "securityDefinitions": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 119cbc0..e815303 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -78,6 +78,8 @@ definitions: type: object model.FileBatch: properties: + content: + type: string created_at: type: string download_count: @@ -102,6 +104,9 @@ definitions: status: description: active / expired / deleted type: string + type: + description: file / text + type: string updated_at: type: string type: object @@ -134,6 +139,8 @@ definitions: type: object public.PickupResponse: properties: + content: + type: string download_count: type: integer expire_at: @@ -148,6 +155,8 @@ definitions: type: integer remark: type: string + type: + type: string type: object public.UploadResponse: properties: @@ -158,6 +167,26 @@ definitions: pickup_code: type: string type: object + public.UploadTextRequest: + properties: + content: + example: 这是一段长文本内容... + type: string + expire_days: + example: 7 + type: integer + expire_type: + example: time + type: string + max_downloads: + example: 5 + type: integer + remark: + example: 文本备注 + type: string + required: + - content + type: object info: contact: email: support@swagger.io @@ -545,6 +574,41 @@ paths: summary: 上传文件 tags: - Public + /api/upload/text: + post: + consumes: + - application/json + description: 中转一段长文本内容并创建一个提取批次 + parameters: + - description: 文本内容及配置 + in: body + name: request + required: true + schema: + $ref: '#/definitions/public.UploadTextRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/model.Response' + - properties: + data: + $ref: '#/definitions/public.UploadResponse' + type: object + "400": + description: Bad Request + schema: + $ref: '#/definitions/model.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/model.Response' + summary: 发送长文本 + tags: + - Public securityDefinitions: AdminAuth: description: Type "Bearer " to authenticate. diff --git a/internal/api/public/pickup.go b/internal/api/public/pickup.go index c5a0242..665c575 100644 --- a/internal/api/public/pickup.go +++ b/internal/api/public/pickup.go @@ -22,7 +22,9 @@ type PickupResponse struct { ExpireType string `json:"expire_type"` DownloadCount int `json:"download_count"` MaxDownloads int `json:"max_downloads"` - Files []model.FileItem `json:"files"` + Type string `json:"type"` + Content string `json:"content,omitempty"` + Files []model.FileItem `json:"files,omitempty"` } // DownloadBatch 批量下载文件 (ZIP) @@ -107,6 +109,8 @@ func (h *PickupHandler) Pickup(c *gin.Context) { ExpireType: batch.ExpireType, DownloadCount: batch.DownloadCount, MaxDownloads: batch.MaxDownloads, + Type: batch.Type, + Content: batch.Content, Files: batch.FileItems, })) } diff --git a/internal/api/public/upload.go b/internal/api/public/upload.go index 5d4da1b..abeb784 100644 --- a/internal/api/public/upload.go +++ b/internal/api/public/upload.go @@ -94,3 +94,60 @@ func (h *UploadHandler) Upload(c *gin.Context) { BatchID: batch.ID, })) } + +type UploadTextRequest struct { + Content string `json:"content" binding:"required" example:"这是一段长文本内容..."` + Remark string `json:"remark" example:"文本备注"` + ExpireType string `json:"expire_type" example:"time"` + ExpireDays int `json:"expire_days" example:"7"` + MaxDownloads int `json:"max_downloads" example:"5"` +} + +// UploadText 发送长文本并生成取件码 +// @Summary 发送长文本 +// @Description 中转一段长文本内容并创建一个提取批次 +// @Tags Public +// @Accept json +// @Produce json +// @Param request body UploadTextRequest true "文本内容及配置" +// @Success 200 {object} model.Response{data=UploadResponse} +// @Failure 400 {object} model.Response +// @Failure 500 {object} model.Response +// @Router /api/upload/text [post] +func (h *UploadHandler) UploadText(c *gin.Context) { + var req UploadTextRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, model.ErrorResponse(model.CodeBadRequest, err.Error())) + return + } + + if req.ExpireType == "" { + req.ExpireType = "time" + } + + var expireValue interface{} + switch req.ExpireType { + case "time": + if req.ExpireDays <= 0 { + req.ExpireDays = config.GlobalConfig.Upload.MaxRetentionDays + } + expireValue = req.ExpireDays + case "download": + if req.MaxDownloads <= 0 { + req.MaxDownloads = 1 + } + expireValue = req.MaxDownloads + } + + batch, err := h.uploadService.CreateTextBatch(c.Request.Context(), req.Content, req.Remark, req.ExpireType, expireValue) + if err != nil { + c.JSON(http.StatusInternalServerError, model.ErrorResponse(model.CodeInternalError, err.Error())) + return + } + + c.JSON(http.StatusOK, model.SuccessResponse(UploadResponse{ + PickupCode: batch.PickupCode, + ExpireAt: batch.ExpireAt, + BatchID: batch.ID, + })) +} diff --git a/internal/model/file_batch.go b/internal/model/file_batch.go index b723789..33f19b8 100644 --- a/internal/model/file_batch.go +++ b/internal/model/file_batch.go @@ -15,6 +15,8 @@ type FileBatch struct { MaxDownloads int `json:"max_downloads"` DownloadCount int `gorm:"default:0" json:"download_count"` Status string `gorm:"default:'active'" json:"status"` // active / expired / deleted + Type string `gorm:"default:'file'" json:"type"` // file / text + Content string `json:"content,omitempty"` FileItems []FileItem `gorm:"foreignKey:BatchID" json:"file_items,omitempty"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` diff --git a/internal/service/upload_service.go b/internal/service/upload_service.go index b55ab71..085a367 100644 --- a/internal/service/upload_service.go +++ b/internal/service/upload_service.go @@ -38,19 +38,10 @@ func (s *UploadService) CreateBatch(ctx context.Context, files []*multipart.File Remark: remark, ExpireType: expireType, Status: "active", + Type: "file", } - switch expireType { - case "time": - if days, ok := expireValue.(int); ok { - expireAt := time.Now().Add(time.Duration(days) * 24 * time.Hour) - batch.ExpireAt = &expireAt - } - case "download": - if max, ok := expireValue.(int); ok { - batch.MaxDownloads = max - } - } + s.applyExpire(batch, expireType, expireValue) // 3. 处理文件上传 return batch, s.db.Transaction(func(tx *gorm.DB) error { @@ -69,6 +60,47 @@ func (s *UploadService) CreateBatch(ctx context.Context, files []*multipart.File }) } +func (s *UploadService) CreateTextBatch(ctx context.Context, content string, remark string, expireType string, expireValue interface{}) (*model.FileBatch, error) { + // 1. 生成取件码 + pickupCode, err := s.generatePickupCode(config.GlobalConfig.Security.PickupCodeLength) + if err != nil { + return nil, err + } + + // 2. 准备 Batch + batch := &model.FileBatch{ + PickupCode: pickupCode, + Remark: remark, + ExpireType: expireType, + Status: "active", + Type: "text", + Content: content, + } + + s.applyExpire(batch, expireType, expireValue) + + // 3. 保存 + if err := s.db.Create(batch).Error; err != nil { + return nil, err + } + + return batch, nil +} + +func (s *UploadService) applyExpire(batch *model.FileBatch, expireType string, expireValue interface{}) { + switch expireType { + case "time": + if days, ok := expireValue.(int); ok { + expireAt := time.Now().Add(time.Duration(days) * 24 * time.Hour) + batch.ExpireAt = &expireAt + } + case "download": + if max, ok := expireValue.(int); ok { + batch.MaxDownloads = max + } + } +} + func (s *UploadService) processFile(ctx context.Context, tx *gorm.DB, batchID uint, fileHeader *multipart.FileHeader) (*model.FileItem, error) { file, err := fileHeader.Open() if err != nil { diff --git a/main.go b/main.go index 6fa2818..7378701 100644 --- a/main.go +++ b/main.go @@ -70,6 +70,7 @@ func main() { api := r.Group("/api") { api.POST("/upload", uploadHandler.Upload) + api.POST("/upload/text", uploadHandler.UploadText) api.GET("/pickup/:pickup_code", middleware.PickupRateLimit(), pickupHandler.Pickup) api.GET("/download/file/:file_id", pickupHandler.DownloadFile) api.GET("/download/batch/:pickup_code", pickupHandler.DownloadBatch)