Files
backend/internal/handler/upload_handler.go
lafay a0e210feab
All checks were successful
Build Backend / build (push) Successful in 3m1s
Build Backend / build-docker (push) Successful in 2m10s
feat(server): implement chat file TTL cleanup and enhance JPush vendor channel support
Add FileCleanupWorker for automatic expiration of chat files with configurable retention period and batch processing. Files uploaded via `/api/v1/uploads/files` are tracked in `uploaded_files` table with expiration timestamps, then deleted from S3 and database upon expiry. Expired file URLs are injected as `"expired": true` in message responses.

Extend JPush push notification configuration with vendor-specific channel_id support (xiaomi, huawei, oppo, vivo, meizu, honor, fcm) for differentiating system vs chat notifications. Add iOS APNs thread-id grouping configuration for notification categorization.
2026-06-17 20:41:55 +08:00

131 lines
2.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package handler
import (
"github.com/gin-gonic/gin"
"with_you/internal/pkg/response"
"with_you/internal/service"
)
// UploadHandler 上传处理器
type UploadHandler struct {
uploadService service.UploadService
}
// NewUploadHandler 创建上传处理器
func NewUploadHandler(uploadService service.UploadService) *UploadHandler {
return &UploadHandler{
uploadService: uploadService,
}
}
// UploadImage 上传图片
func (h *UploadHandler) UploadImage(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
response.Unauthorized(c, "")
return
}
file, err := c.FormFile("image")
if err != nil {
response.BadRequest(c, "image file is required")
return
}
url, previewURL, previewURLLarge, err := h.uploadService.UploadImage(c.Request.Context(), file)
if err != nil {
response.InternalServerError(c, "failed to upload image")
return
}
response.Success(c, gin.H{
"url": url,
"preview_url": previewURL,
"preview_url_large": previewURLLarge,
})
}
// UploadAvatar 上传头像
func (h *UploadHandler) UploadAvatar(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
response.Unauthorized(c, "")
return
}
file, err := c.FormFile("image")
if err != nil {
response.BadRequest(c, "avatar file is required")
return
}
url, err := h.uploadService.UploadAvatar(c.Request.Context(), userID, file)
if err != nil {
response.InternalServerError(c, "failed to upload avatar")
return
}
response.Success(c, gin.H{"url": url})
}
// UploadCover 上传头图(个人主页封面)
func (h *UploadHandler) UploadCover(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
response.Unauthorized(c, "")
return
}
file, err := c.FormFile("image")
if err != nil {
response.BadRequest(c, "image file is required")
return
}
url, err := h.uploadService.UploadCover(c.Request.Context(), userID, file)
if err != nil {
response.InternalServerError(c, "failed to upload cover")
return
}
response.Success(c, gin.H{"url": url})
}
// UploadFile 上传聊天/通用文件(非图片类资源)
// form 字段file必填、folder可选默认 chat
func (h *UploadHandler) UploadFile(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
response.Unauthorized(c, "")
return
}
file, err := c.FormFile("file")
if err != nil {
response.BadRequest(c, "file is required")
return
}
folder := c.DefaultPostForm("folder", "chat")
result, err := h.uploadService.UploadFile(c.Request.Context(), file, folder, userID)
if err != nil {
// 参数类错误(大小/类型)返回 400其余返回 500
if file.Size > 0 && file.Size <= 100<<20 {
response.BadRequest(c, err.Error())
return
}
response.InternalServerError(c, "failed to upload file")
return
}
response.Success(c, gin.H{
"url": result.URL,
"name": result.Name,
"size": result.Size,
"mime_type": result.MimeType,
})
}