Files
likedream/internal/application/cpdaily/imageuploader/upload.go
T
sugarBecomer af7fcc4cb4 🐛 销假
2025-12-08 17:54:05 +08:00

218 lines
6.6 KiB
Go

package imageuploader
import (
"context"
"encoding/json"
"likedream-goframe/internal/application/cpdaily/types"
"likedream-goframe/internal/application/filestorage"
"likedream-goframe/internal/common/db"
"likedream-goframe/internal/common/errors"
"likedream-goframe/internal/ent"
"likedream-goframe/internal/ent/cpimage"
"likedream-goframe/internal/utils"
"github.com/bytedance/sonic"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/google/uuid"
"github.com/samber/oops"
"resty.dev/v3"
)
// CpImageUploadPolicy 结构体用于表示图片上传策略的相关信息
type CpImageUploadPolicy struct {
AccessId string `json:"accessid"`
FileName string `json:"fileName"`
Policy string `json:"policy"`
Host string `json:"host"`
Signature string `json:"signature"`
}
func UploadImageFromStorage(ctx context.Context, host string, imageId uuid.UUID, taskType types.TaskType, client *resty.Client) string {
targetImage, e := db.Global.CpImage.Query().
Where(cpimage.ImageID(imageId)).
Select(cpimage.FieldKey, cpimage.FieldContentType).
Only(ctx)
if e != nil {
if ent.IsNotFound(e) {
utils.OkOrPanic(errors.NewBusinessError(503, "图片不存在"), oops.WithContext(ctx).Wrap)
}
utils.OkOrPanic(e, oops.WithContext(ctx).Wrap)
}
imageUploadPolicy := GetUploadPolicyAsync(ctx, host, taskType, client)
targetFile := filestorage.GlobalS3.GetFile(ctx, targetImage.Key)
resp, e := client.R().
SetMultipartFields(&resty.MultipartField{
Name: "x-obs-acl",
Values: []string{"public-read"},
}, &resty.MultipartField{
Name: "AccessKeyId",
Values: []string{imageUploadPolicy.AccessId},
}, &resty.MultipartField{
Name: "key",
Values: []string{imageUploadPolicy.FileName},
}, &resty.MultipartField{
Name: "signature",
Values: []string{imageUploadPolicy.Signature},
}, &resty.MultipartField{
Name: "policy",
Values: []string{imageUploadPolicy.Policy},
}, &resty.MultipartField{
Name: "content-type",
Values: []string{targetImage.ContentType},
}, &resty.MultipartField{
Name: "file",
FileName: "file",
Reader: targetFile,
}).
Post(imageUploadPolicy.Host)
utils.OkOrPanic(e, oops.WithContext(ctx).Wrap)
if resp.StatusCode() > 299 {
utils.OkOrPanic(errors.NewBusinessError(503, "上传图片失败: "+utils.ReadString(resp.Body)), oops.WithContext(ctx).With("response", utils.ReadString(resp.Body)).Wrap)
}
previewUrl := GetPreviewUrlAsync(ctx, host, imageUploadPolicy.FileName, taskType, client)
return previewUrl
}
func UploadImageFromFile(ctx context.Context, host string, file *ghttp.UploadFile, taskType types.TaskType, client *resty.Client) string {
imageUploadPolicy := GetUploadPolicyAsync(ctx, host, taskType, client)
open, _ := file.Open()
resp, e := client.R().
SetMultipartFields(&resty.MultipartField{
Name: "x-obs-acl",
Values: []string{"public-read"},
}, &resty.MultipartField{
Name: "AccessKeyId",
Values: []string{imageUploadPolicy.AccessId},
}, &resty.MultipartField{
Name: "key",
Values: []string{imageUploadPolicy.FileName},
}, &resty.MultipartField{
Name: "signature",
Values: []string{imageUploadPolicy.Signature},
}, &resty.MultipartField{
Name: "policy",
Values: []string{imageUploadPolicy.Policy},
}, &resty.MultipartField{
Name: "content-type",
Values: []string{file.Header.Get("Content-Type")},
}, &resty.MultipartField{
Name: "file",
FileName: "file",
Reader: open,
}).
Post(imageUploadPolicy.Host)
utils.OkOrPanic(e, oops.WithContext(ctx).Wrap)
if resp.StatusCode() > 299 {
utils.OkOrPanic(errors.NewBusinessError(503, "上传图片失败: "+utils.ReadString(resp.Body)), oops.WithContext(ctx).With("response", utils.ReadString(resp.Body)).Wrap)
}
previewUrl := GetPreviewUrlAsync(ctx, host, imageUploadPolicy.FileName, taskType, client)
return previewUrl
}
// GetPreviewUrlAsync 用于获取图片预览地址
func GetPreviewUrlAsync(ctx context.Context, host string, fileKey string, taskType types.TaskType, client *resty.Client) string {
var apiPath string
if taskType == types.TaskTypeSleepCheck {
apiPath = "/previewAttachment"
} else if taskType == types.TaskTypeLeave {
apiPath = "/obs/previewAttachment"
} else {
apiPath = "/sign/previewAttachment"
}
urlPrefix := GetImageUploadUrlPrefix(ctx, taskType)
fullUrl := host + urlPrefix + apiPath
reqBody := map[string]interface{}{
"ossKey": fileKey,
}
resp, err := client.R().
SetBody(reqBody).
Post(fullUrl)
utils.OkOrPanic(err, oops.WithContext(ctx).Wrap)
defer resp.Body.Close()
responseJSON := utils.ReadJson[map[string]*json.RawMessage](resp.Body)
datas := responseJSON["datas"]
if datas == nil {
utils.OkOrPanic(errors.NewBusinessError(404, "上传图片URL为空"), oops.WithContext(ctx).With("response", responseJSON).Wrap)
}
var previewUrl string
utils.OkOrPanic(sonic.Unmarshal(*datas, &previewUrl), oops.WithContext(ctx).Wrap)
return previewUrl
}
// GetUploadPolicyAsync 用于获取图片上传策略
func GetUploadPolicyAsync(ctx context.Context, host string, taskType types.TaskType, client *resty.Client) *CpImageUploadPolicy {
// 构造请求路径
urlPrefix := GetImageUploadUrlPrefix(ctx, taskType)
var apiPath string
if taskType == types.TaskTypeSleepCheck {
apiPath = "/getUploadPolicy"
} else {
apiPath = "/obs/getUploadPolicy"
}
fullUrl := host + urlPrefix + apiPath
// 构造请求体
reqBody := map[string]interface{}{
"fileType": 1,
}
// 构造请求
req := client.R().SetBody(reqBody)
// 发送请求
resp, err := req.Post(fullUrl)
if err != nil {
utils.OkOrPanic(err, oops.WithContext(ctx).Wrap)
}
defer resp.Body.Close()
responseJSON := utils.ReadJson[map[string]*json.RawMessage](resp.Body)
datas := responseJSON["datas"]
if datas == nil {
utils.OkOrPanic(errors.NewBusinessError(503, "获取图片上传策略失败: 响应中缺少 datas 字段"), oops.WithContext(ctx).With("response", responseJSON).Wrap)
}
var policy CpImageUploadPolicy
utils.OkOrPanic(sonic.Unmarshal(*datas, &policy), oops.WithContext(ctx).Wrap)
return &policy
}
// GetImageUploadUrlPrefix 根据任务类型返回图片上传的URL前缀
func GetImageUploadUrlPrefix(ctx context.Context, taskType types.TaskType) string {
switch taskType {
case types.TaskTypeSleepCheck:
return "/wec-counselor-attendance-apps/student/attendance"
case types.TaskTypeSign:
return "/wec-counselor-sign-apps/stu"
case types.TaskTypeCollection:
return "/wec-counselor-collector-apps/student/collector"
case types.TaskTypeLeave:
return "/wec-counselor-leave-apps/leave"
default:
utils.OkOrPanic(errors.NewBusinessError(503, "未知的任务类型"), oops.WithContext(ctx).Wrap)
panic("unreachable")
}
}