mirror of https://github.com/rocboss/paopao-ce
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
300 lines
7.5 KiB
300 lines
7.5 KiB
2 years ago
|
package api
|
||
|
|
||
|
import (
|
||
|
"image"
|
||
|
"net/url"
|
||
|
"strings"
|
||
|
|
||
|
"github.com/aliyun/aliyun-oss-go-sdk/oss"
|
||
|
"github.com/disintegration/imaging"
|
||
|
"github.com/gin-gonic/gin"
|
||
|
"github.com/gofrs/uuid"
|
||
2 years ago
|
"github.com/rocboss/paopao-ce/global"
|
||
|
"github.com/rocboss/paopao-ce/internal/model"
|
||
|
"github.com/rocboss/paopao-ce/internal/service"
|
||
|
"github.com/rocboss/paopao-ce/pkg/app"
|
||
|
"github.com/rocboss/paopao-ce/pkg/convert"
|
||
|
"github.com/rocboss/paopao-ce/pkg/errcode"
|
||
2 years ago
|
)
|
||
|
|
||
|
func GeneratePath(s string) string {
|
||
|
n := len(s)
|
||
|
if n <= 2 {
|
||
|
return s
|
||
|
}
|
||
|
|
||
|
return GeneratePath(s[:n-2]) + "/" + s[n-2:]
|
||
|
}
|
||
|
|
||
|
func GetFileExt(s string) (string, error) {
|
||
|
switch s {
|
||
|
case "image/png":
|
||
|
return ".png", nil
|
||
|
case "image/jpg":
|
||
|
return ".jpg", nil
|
||
|
case "image/jpeg":
|
||
|
return ".jpeg", nil
|
||
|
case "image/gif":
|
||
|
return ".gif", nil
|
||
|
case "video/mp4":
|
||
|
return ".mp4", nil
|
||
|
case "video/quicktime":
|
||
|
return ".mov", nil
|
||
|
case "application/zip":
|
||
|
return ".zip", nil
|
||
|
default:
|
||
|
return "", errcode.FileInvalidExt.WithDetails("仅允许 png/jpg/gif/mp4/mov/zip 类型")
|
||
|
}
|
||
|
}
|
||
|
func GetImageSize(img image.Rectangle) (int, int) {
|
||
|
b := img.Bounds()
|
||
|
width := b.Max.X
|
||
|
height := b.Max.Y
|
||
|
return width, height
|
||
|
}
|
||
|
|
||
|
func UploadAttachment(c *gin.Context) {
|
||
|
response := app.NewResponse(c)
|
||
|
svc := service.New(c)
|
||
|
|
||
|
uploadType := c.Request.FormValue("type")
|
||
|
file, fileHeader, err := c.Request.FormFile("file")
|
||
|
if err != nil {
|
||
|
global.Logger.Errorf("api.UploadAttachment err: %v", err)
|
||
|
response.ToErrorResponse(errcode.FileUploadFailed)
|
||
|
return
|
||
|
}
|
||
|
defer file.Close()
|
||
|
|
||
|
if uploadType != "public/video" &&
|
||
|
uploadType != "public/image" &&
|
||
|
uploadType != "public/avatar" &&
|
||
|
uploadType != "attachment" {
|
||
|
response.ToErrorResponse(errcode.InvalidParams)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if fileHeader.Size > 1024*1024*100 {
|
||
|
response.ToErrorResponse(errcode.FileInvalidSize.WithDetails("最大允许100MB"))
|
||
|
return
|
||
|
}
|
||
|
|
||
|
fileExt, err := GetFileExt(fileHeader.Header.Get("Content-Type"))
|
||
|
if err != nil {
|
||
|
global.Logger.Errorf("GetFileExt err: %v", err)
|
||
|
response.ToErrorResponse(err.(*errcode.Error))
|
||
|
return
|
||
|
}
|
||
|
|
||
|
fileReader, err := fileHeader.Open()
|
||
|
if err != nil {
|
||
|
global.Logger.Errorf("Attachment file read err: %v", err)
|
||
|
response.ToErrorResponse(errcode.FileUploadFailed)
|
||
|
return
|
||
|
}
|
||
|
defer fileReader.Close()
|
||
|
|
||
|
// 生成随机路径
|
||
|
randomPath := uuid.Must(uuid.NewV4()).String()
|
||
|
ossSavePath := uploadType + "/" + GeneratePath(randomPath[:8]) + "/" + randomPath[9:] + fileExt
|
||
|
|
||
|
client, err := oss.New(global.AliossSetting.AliossEndpoint, global.AliossSetting.AliossAccessKeyID, global.AliossSetting.AliossAccessKeySecret)
|
||
|
if err != nil {
|
||
|
global.Logger.Errorf("oss.New err: %v", err)
|
||
|
response.ToErrorResponse(errcode.FileUploadFailed)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
bucket, err := client.Bucket("paopao-assets")
|
||
|
if err != nil {
|
||
|
global.Logger.Errorf("client.Bucket err: %v", err)
|
||
|
response.ToErrorResponse(errcode.FileUploadFailed)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
err = bucket.PutObject(ossSavePath, fileReader)
|
||
|
if err != nil {
|
||
|
global.Logger.Errorf("bucket.PutObject err: %v", err)
|
||
|
response.ToErrorResponse(errcode.FileUploadFailed)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// 构造附件Model
|
||
|
attachment := &model.Attachment{
|
||
|
FileSize: fileHeader.Size,
|
||
|
Content: "https://" + global.AliossSetting.AliossDomain + "/" + ossSavePath,
|
||
|
}
|
||
|
|
||
|
if userID, exists := c.Get("UID"); exists {
|
||
|
attachment.UserID = userID.(int64)
|
||
|
}
|
||
|
|
||
|
if uploadType == "public/image" || uploadType == "public/avatar" {
|
||
|
attachment.Type = model.ATTACHMENT_TYPE_IMAGE
|
||
|
|
||
|
src, err := imaging.Decode(file)
|
||
|
if err == nil {
|
||
|
attachment.ImgWidth, attachment.ImgHeight = GetImageSize(src.Bounds())
|
||
|
}
|
||
|
}
|
||
|
if uploadType == "public/video" {
|
||
|
attachment.Type = model.ATTACHMENT_TYPE_VIDEO
|
||
|
}
|
||
|
if uploadType == "attachment" {
|
||
|
attachment.Type = model.ATTACHMENT_TYPE_OTHER
|
||
|
}
|
||
|
|
||
|
attachment, err = svc.CreateAttachment(attachment)
|
||
|
if err != nil {
|
||
|
global.Logger.Errorf("svc.CreateAttachment err: %v", err)
|
||
|
response.ToErrorResponse(errcode.FileUploadFailed)
|
||
|
}
|
||
|
|
||
|
response.ToResponse(attachment)
|
||
|
}
|
||
|
|
||
|
func DownloadAttachmentPrecheck(c *gin.Context) {
|
||
|
response := app.NewResponse(c)
|
||
|
svc := service.New(c)
|
||
|
|
||
|
contentID := convert.StrTo(c.Query("id")).MustInt64()
|
||
|
// 加载content
|
||
|
content, err := svc.GetPostContentByID(contentID)
|
||
|
if err != nil {
|
||
|
global.Logger.Errorf("svc.GetPostContentByID err: %v", err)
|
||
|
response.ToErrorResponse(errcode.InvalidDownloadReq)
|
||
|
}
|
||
|
user, _ := c.Get("USER")
|
||
|
if content.Type == model.CONTENT_TYPE_CHARGE_ATTACHMENT {
|
||
|
// 加载post
|
||
|
post, err := svc.GetPost(content.PostID)
|
||
|
if err != nil {
|
||
|
global.Logger.Errorf("svc.GetPost err: %v", err)
|
||
|
response.ToResponse(gin.H{
|
||
|
"paid": false,
|
||
|
})
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// 发布者或管理员免费下载
|
||
|
if post.UserID == user.(*model.User).ID || user.(*model.User).IsAdmin {
|
||
|
response.ToResponse(gin.H{
|
||
|
"paid": true,
|
||
|
})
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// 检测是否有购买记录
|
||
|
response.ToResponse(gin.H{
|
||
|
"paid": svc.CheckPostAttachmentIsPaid(post.ID, user.(*model.User).ID),
|
||
|
})
|
||
|
return
|
||
|
}
|
||
|
response.ToResponse(gin.H{
|
||
|
"paid": false,
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func DownloadAttachment(c *gin.Context) {
|
||
|
response := app.NewResponse(c)
|
||
|
svc := service.New(c)
|
||
|
|
||
|
contentID := convert.StrTo(c.Query("id")).MustInt64()
|
||
|
|
||
|
// 加载content
|
||
|
content, err := svc.GetPostContentByID(contentID)
|
||
|
if err != nil {
|
||
|
global.Logger.Errorf("svc.GetPostContentByID err: %v", err)
|
||
|
response.ToErrorResponse(errcode.InvalidDownloadReq)
|
||
|
}
|
||
|
|
||
|
// 收费附件
|
||
|
if content.Type == model.CONTENT_TYPE_CHARGE_ATTACHMENT {
|
||
|
user, _ := c.Get("USER")
|
||
|
|
||
|
// 加载post
|
||
|
post, err := svc.GetPost(content.PostID)
|
||
|
if err != nil {
|
||
|
global.Logger.Errorf("svc.GetPost err: %v", err)
|
||
|
response.ToResponse(gin.H{
|
||
|
"paid": false,
|
||
|
})
|
||
|
return
|
||
|
}
|
||
|
|
||
|
paidFlag := false
|
||
|
|
||
|
// 发布者或管理员免费下载
|
||
|
if post.UserID == user.(*model.User).ID || user.(*model.User).IsAdmin {
|
||
|
paidFlag = true
|
||
|
}
|
||
|
|
||
|
// 检测是否有购买记录
|
||
|
if svc.CheckPostAttachmentIsPaid(post.ID, user.(*model.User).ID) {
|
||
|
paidFlag = true
|
||
|
}
|
||
|
|
||
|
if !paidFlag {
|
||
|
// 未购买,则尝试购买
|
||
|
err := svc.BuyPostAttachment(&model.Post{
|
||
|
Model: &model.Model{
|
||
|
ID: post.ID,
|
||
|
},
|
||
|
UserID: post.UserID,
|
||
|
AttachmentPrice: post.AttachmentPrice,
|
||
|
}, user.(*model.User))
|
||
|
if err != nil {
|
||
|
global.Logger.Errorf("svc.BuyPostAttachment err: %v", err)
|
||
|
if err == errcode.InsuffientDownloadMoney {
|
||
|
|
||
|
response.ToErrorResponse(errcode.InsuffientDownloadMoney)
|
||
|
} else {
|
||
|
|
||
|
response.ToErrorResponse(errcode.DownloadExecFail)
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 开始构造下载地址
|
||
|
client, err := oss.New(global.AliossSetting.AliossEndpoint, global.AliossSetting.AliossAccessKeyID, global.AliossSetting.AliossAccessKeySecret)
|
||
|
if err != nil {
|
||
|
global.Logger.Errorf("oss.New err: %v", err)
|
||
|
response.ToErrorResponse(errcode.DownloadReqError)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
bucket, err := client.Bucket("paopao-assets")
|
||
|
if err != nil {
|
||
|
global.Logger.Errorf("client.Bucket err: %v", err)
|
||
|
response.ToErrorResponse(errcode.DownloadReqError)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// 签名
|
||
|
objectKey := strings.Replace(content.Content, "https://"+global.AliossSetting.AliossDomain+"/", "", -1)
|
||
|
signedURL, err := bucket.SignURL(objectKey, oss.HTTPGet, 60)
|
||
|
if err != nil {
|
||
|
global.Logger.Errorf("client.SignURL err: %v", err)
|
||
|
response.ToErrorResponse(errcode.DownloadReqError)
|
||
|
return
|
||
|
}
|
||
|
ur, err := url.Parse(signedURL)
|
||
|
if err != nil {
|
||
|
global.Logger.Errorf("url.Parse err: %v", err)
|
||
|
response.ToErrorResponse(errcode.DownloadReqError)
|
||
|
return
|
||
|
}
|
||
|
epath, err := url.PathUnescape(ur.Path)
|
||
|
if err != nil {
|
||
|
global.Logger.Errorf("url.PathUnescape err: %v", err)
|
||
|
response.ToErrorResponse(errcode.DownloadReqError)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
ur.Path = epath
|
||
|
ur.RawPath = epath
|
||
|
response.ToResponse(ur.String())
|
||
|
}
|