diff --git a/server/upload/internal/handler/routes.go b/server/upload/internal/handler/routes.go index 03536a1d..7c70b9da 100644 --- a/server/upload/internal/handler/routes.go +++ b/server/upload/internal/handler/routes.go @@ -52,6 +52,11 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { Path: "/api/upload/up-logo", Handler: UploadLogoHandler(serverCtx), }, + { + Method: http.MethodPost, + Path: "/api/upload/up-logo-debug", + Handler: UploadLogoDebugHandler(serverCtx), + }, { Method: http.MethodPost, Path: "/api/upload/upload-file-base", diff --git a/server/upload/internal/handler/uploadlogodebughandler.go b/server/upload/internal/handler/uploadlogodebughandler.go new file mode 100644 index 00000000..0726431b --- /dev/null +++ b/server/upload/internal/handler/uploadlogodebughandler.go @@ -0,0 +1,35 @@ +package handler + +import ( + "net/http" + "reflect" + + "fusenapi/utils/basic" + + "fusenapi/server/upload/internal/logic" + "fusenapi/server/upload/internal/svc" + "fusenapi/server/upload/internal/types" +) + +func UploadLogoDebugHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + + var req types.UploadLogoDebugReq + userinfo, err := basic.RequestParse(w, r, svcCtx, &req) + if err != nil { + return + } + + // 创建一个业务逻辑层实例 + l := logic.NewUploadLogoDebugLogic(r, svcCtx) + + rl := reflect.ValueOf(l) + basic.BeforeLogic(w, r, rl) + + resp := l.UploadLogoDebug(&req, userinfo) + + if !basic.AfterLogic(w, r, rl, resp) { + basic.NormalAfterLogic(w, r, resp) + } + } +} diff --git a/server/upload/internal/logic/uploadlogodebuglogic.go b/server/upload/internal/logic/uploadlogodebuglogic.go new file mode 100644 index 00000000..49122b8f --- /dev/null +++ b/server/upload/internal/logic/uploadlogodebuglogic.go @@ -0,0 +1,253 @@ +package logic + +import ( + "encoding/json" + "errors" + "fmt" + "fusenapi/model/gmodel" + "fusenapi/service/repositories" + "fusenapi/utils/auth" + "fusenapi/utils/basic" + "fusenapi/utils/file" + "fusenapi/utils/hash" + "fusenapi/utils/s3url_to_s3id" + "io" + "net/http" + + "context" + + "fusenapi/server/upload/internal/svc" + "fusenapi/server/upload/internal/types" + + "github.com/zeromicro/go-zero/core/logx" + "gorm.io/gorm" +) + +type UploadLogoDebugLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext + r *http.Request +} + +func NewUploadLogoDebugLogic(r *http.Request, svcCtx *svc.ServiceContext) *UploadLogoDebugLogic { + return &UploadLogoDebugLogic{ + Logger: logx.WithContext(r.Context()), + ctx: r.Context(), + svcCtx: svcCtx, + r: r, + } +} + +// 处理进入前逻辑w,r +// func (l *UploadLogoDebugLogic) BeforeLogic(w http.ResponseWriter, r *http.Request) { +// } + +func (l *UploadLogoDebugLogic) UploadLogoDebug(req *types.UploadLogoDebugReq, userinfo *auth.UserInfo) (resp *basic.Response) { + // 返回值必须调用Set重新返回, resp可以空指针调用 resp.SetStatus(basic.CodeOK, data) + // userinfo 传入值时, 一定不为null + + if userinfo.IsOnlooker() { + // 如果是,返回未授权的错误码 + return resp.SetStatus(basic.CodeUnAuth) + } + + var ( + productTemplateTags []gmodel.FsProductTemplateTags + err error + ) + + var userId int64 + var guestId int64 + + // 检查用户是否是游客 + if userinfo.IsGuest() { + // 如果是,使用游客ID和游客键名格式 + guestId = userinfo.GuestId + } else { + // 否则,使用用户ID和用户键名格式 + userId = userinfo.UserId + } + + //设置内存大小 + l.r.ParseMultipartForm(32 << 20) + + fileObject, fileHeader, err := l.r.FormFile("file") + if err != nil { + logx.Error(err) + return resp.SetStatus(basic.CodeFileUploadErr, "file upload err,no files") + } + defer fileObject.Close() + + // 获取文件的MIME类型 + fileType := fileHeader.Header.Get("Content-Type") + var imageTypes = make(map[string]struct{}, 7) + imageTypes["image/jpg"] = struct{}{} + imageTypes["image/jpeg"] = struct{}{} + imageTypes["image/png"] = struct{}{} + imageTypes["image/gif"] = struct{}{} + imageTypes["image/bmp"] = struct{}{} + imageTypes["image/tiff"] = struct{}{} + imageTypes["image/webp"] = struct{}{} + imageTypes["image/svg+xml"] = struct{}{} + + // 判断文件类型是否为图片 + _, ok := imageTypes[fileType] + if !ok { + return resp.SetStatus(basic.CodeFileUploadErr, "file upload err,file is not image") + } + + // 限制上传文件大小 50k + // maxSize := 100 * 1024 + // if fileHeader.Size > int64(maxSize) { + // return resp.SetStatus(basic.CodeFileUploadErr, "file upload err,The file size exceeds the maximum limit of 100k") + // } + + // 读取数据流 + ioData, err := io.ReadAll(fileObject) + if err != nil { + logx.Error(err) + return resp.SetStatus(basic.CodeFileUploadErr, "file upload err,no files") + } + + // 上传文件 + var upload = file.Upload{ + Ctx: l.ctx, + MysqlConn: l.svcCtx.MysqlConn, + AwsSession: l.svcCtx.AwsSession, + } + var resourceId string = hash.JsonHashKey(req.FileKey) + uploadRes, err := upload.UploadFileByByte(&file.UploadBaseReq{ + FileHash: resourceId, + FileByte: ioData, + UploadBucket: 1, + ApiType: 2, + UserId: userId, + GuestId: guestId, + Source: "upload-logo", + }) + + if err != nil { + logx.Error(err) + basic.CodeServiceErr.Message = fmt.Sprintf("算法请求--LOGO信息--错误:%+v", err) + return resp.SetStatus(basic.CodeServiceErr) + } + + userinfo.Debug = &auth.Debug{ + IsAllTemplateTag: 1, + } + var resultStr string + resLogoStandard, err := l.svcCtx.Repositories.ImageHandle.LogoInfoSet(l.ctx, &repositories.LogoInfoSetReq{ + LogoUrl: uploadRes.ResourceUrl, + Version: l.svcCtx.Config.BLMService.Version, + Debug: userinfo.Debug, + }) + + if err != nil { + logx.Error(err) + basic.CodeServiceErr.Message = fmt.Sprintf("算法请求--LOGO信息--错误:%+v", err) + return resp.SetStatus(basic.CodeServiceErr, fmt.Sprintf("算法请求--LOGO信息--错误:%+v", err)) + } + resultStr = resLogoStandard.Res + + //解析用户素材元数据 + var metaData map[string]interface{} + if err = json.Unmarshal([]byte(resultStr), &metaData); err != nil { + logx.Error(err) + return resp.SetStatusWithMessage(basic.CodeJsonErr, "failed to parse user metadata") + } + //解析模板标签颜色 + var mapMaterialTemplateTagColors map[string][][]string + b, _ := json.Marshal(metaData["template_tag"]) + if err = json.Unmarshal(b, &mapMaterialTemplateTagColors); err != nil { + logx.Error(err) + return resp.SetStatusWithMessage(basic.CodeJsonErr, "invalid format of metadata`s template_tag") + } + //解析单纯的模板标签用于排序 + var simpleTemplateTags []string + b, _ = json.Marshal(metaData["template_tag_id"]) + if err = json.Unmarshal(b, &simpleTemplateTags); err != nil { + logx.Error(err) + return resp.SetStatusWithMessage(basic.CodeJsonErr, "invalid format of metadata`s template_tag_id") + } + var templateTagNameList []string + for templateTag := range mapMaterialTemplateTagColors { + templateTagNameList = append(templateTagNameList, templateTag) + } + productTemplateTags, err = l.svcCtx.AllModels.FsProductTemplateTags.GetListByTagNames(l.ctx, templateTagNameList, len(templateTagNameList), 1, "id DESC") + if err != nil { + logx.Error(err) + return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "failed to get template tags") + } + //获取默认渲染的产品(写死) + productInfo, err := l.svcCtx.AllModels.FsProduct.FindOne(l.ctx, 30) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return resp.SetStatusWithMessage(basic.CodeDbRecordNotFoundErr, "the product info is not exists") + } + logx.Error(err) + return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "failed to get product info") + } + //资源id集合 + resourceIds := make([]string, 0, 5) + for _, v := range productTemplateTags { + resourceIds = append(resourceIds, s3url_to_s3id.GetS3ResourceIdFormUrl(*v.Cover)) + } + resourceIds = append(resourceIds, s3url_to_s3id.GetS3ResourceIdFormUrl(*productInfo.Cover)) + //根据resourceUrls找到对应的元数据 + resourceMetadataList, err := l.svcCtx.AllModels.FsResource.FindAllByResourceIds(l.ctx, resourceIds) + if err != nil { + logx.Error(err) + return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "failed to get resource list") + } + mapResourceMetadata := make(map[string]map[string]interface{}) + for _, v := range resourceMetadataList { + var metadata map[string]interface{} + if v.Metadata != nil { + _ = json.Unmarshal(*v.Metadata, &metadata) + } + mapResourceMetadata[*v.ResourceUrl] = metadata + } + //排序 + for index, templateTagStr := range simpleTemplateTags { + for k, v := range productTemplateTags { + if templateTagStr == *v.TemplateTag { + productTemplateTags[k], productTemplateTags[index] = productTemplateTags[index], productTemplateTags[k] + break + } + } + } + list := make([]types.GetProductTemplateTagsRsp, 0, len(productTemplateTags)) + for _, templateTagInfo := range productTemplateTags { + colors := make([][]string, 0, 10) + SelectedColorIndex := 0 + isDefaultTemplateTag := false + var templateTagGroups []interface{} + if templateTagInfo.Groups != nil && *templateTagInfo.Groups != "" { + if err = json.Unmarshal([]byte(*templateTagInfo.Groups), &templateTagGroups); err != nil { + logx.Error(err) + return resp.SetStatusWithMessage(basic.CodeJsonErr, "failed to parse groups") + } + } + list = append(list, types.GetProductTemplateTagsRsp{ + Id: templateTagInfo.Id, + TemplateTag: *templateTagInfo.TemplateTag, + IsDefaultTemplateTag: isDefaultTemplateTag, + TemplateTagGroups: templateTagGroups, + Cover: *templateTagInfo.Cover, + CoverMetadata: mapResourceMetadata[*templateTagInfo.Cover], + Colors: colors, + SelectedColorIndex: SelectedColorIndex, + ProductId: productInfo.Id, + ProductCover: *productInfo.Cover, + ProductCoverMetadata: mapResourceMetadata[*productInfo.Cover], + }) + } + + return resp.SetStatusWithMessage(basic.CodeOK, "success", list) +} + +// 处理逻辑后 w,r 如:重定向, resp 必须重新处理 +// func (l *UploadLogoDebugLogic) AfterLogic(w http.ResponseWriter, r *http.Request, resp *basic.Response) { +// // httpx.OkJsonCtx(r.Context(), w, resp) +// } diff --git a/server/upload/internal/types/types.go b/server/upload/internal/types/types.go index 3f496b19..2f1b57ed 100644 --- a/server/upload/internal/types/types.go +++ b/server/upload/internal/types/types.go @@ -5,6 +5,20 @@ import ( "fusenapi/utils/basic" ) +type GetProductTemplateTagsRsp struct { + Id int64 `json:"id"` + TemplateTag string `json:"template_tag"` + IsDefaultTemplateTag bool `json:"is_default_template_tag"` + TemplateTagGroups interface{} `json:"template_tag_groups"` + Cover string `json:"cover"` + CoverMetadata interface{} `json:"cover_metadata"` + Colors [][]string `json:"colors"` + SelectedColorIndex int `json:"selected_color_index"` + ProductId int64 `json:"product_id"` + ProductCover string `json:"product_cover"` + ProductCoverMetadata interface{} `json:"product_cover_metadata"` +} + type UploadLogoStandardReq struct { IsRemoveBg string `form:"is_remove_bg"` LogoFile string `form:"logo_file"` @@ -26,6 +40,13 @@ type UploadFileBaseReq struct { ResourceId string `form:"resource_id,optional"` // 资源ID } +type UploadLogoDebugReq struct { + FileKey string `form:"file_key"` // 上传logo唯一标识信息 + IsRemoveBg int64 `form:"is_remove_bg,optional"` // 是否要去掉背景 + Proportion int64 `form:"proportion,default=60"` // 贴图在模型面板上的比例 + SkuId int64 `form:"sku_id,default=0"` // 模板ID +} + type UploadLogoReq struct { FileKey string `form:"file_key"` // 上传logo唯一标识信息 IsRemoveBg int64 `form:"is_remove_bg,optional"` // 是否要去掉背景 diff --git a/server_api/upload.api b/server_api/upload.api index 3935933f..2c842f68 100644 --- a/server_api/upload.api +++ b/server_api/upload.api @@ -38,6 +38,10 @@ service upload { @handler UploadLogoHandler post /api/upload/up-logo(UploadLogoReq) returns (response); + // 上传LOGO-DEBUG + @handler UploadLogoDebugHandler + post /api/upload/up-logo-debug(UploadLogoDebugReq) returns (response); + // 上传文件发起--base64 @handler UploadFileBaseHandler post /api/upload/upload-file-base(UploadFileBaseReq) returns (response); @@ -47,6 +51,20 @@ service upload { } +type GetProductTemplateTagsRsp { + Id int64 `json:"id"` + TemplateTag string `json:"template_tag"` + IsDefaultTemplateTag bool `json:"is_default_template_tag"` + TemplateTagGroups interface{} `json:"template_tag_groups"` + Cover string `json:"cover"` + CoverMetadata interface{} `json:"cover_metadata"` + Colors [][]string `json:"colors"` + SelectedColorIndex int `json:"selected_color_index"` + ProductId int64 `json:"product_id"` + ProductCover string `json:"product_cover"` + ProductCoverMetadata interface{} `json:"product_cover_metadata"` +} + type ( UploadLogoStandardReq { IsRemoveBg string `form:"is_remove_bg"` @@ -72,6 +90,15 @@ type ( } ) +type ( + UploadLogoDebugReq { + FileKey string `form:"file_key"` // 上传logo唯一标识信息 + IsRemoveBg int64 `form:"is_remove_bg,optional"` // 是否要去掉背景 + Proportion int64 `form:"proportion,default=60"` // 贴图在模型面板上的比例 + SkuId int64 `form:"sku_id,default=0"` // 模板ID + } +) + type ( UploadLogoReq { FileKey string `form:"file_key"` // 上传logo唯一标识信息