增加文本中转功能

This commit is contained in:
2026-01-13 17:07:27 +08:00
parent d2352318f4
commit 9c10dfd496
8 changed files with 370 additions and 12 deletions

View File

@@ -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": {

View File

@@ -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": {

View File

@@ -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 <your-jwt-token>" to authenticate.

View File

@@ -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,
}))
}

View File

@@ -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,
}))
}

View File

@@ -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"`

View File

@@ -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 {

View File

@@ -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)