fusenapi/server/product/internal/logic/getproductdetaillogic.go
laodaming f6f305f0f3 fix
2023-11-01 16:58:26 +08:00

436 lines
17 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 logic
import (
"encoding/json"
"errors"
"fusenapi/constants"
"fusenapi/model/gmodel"
"fusenapi/utils/auth"
"fusenapi/utils/basic"
"fusenapi/utils/color_list"
"fusenapi/utils/format"
"fusenapi/utils/s3url_to_s3id"
"fusenapi/utils/template_switch_info"
"gorm.io/gorm"
"reflect"
"strings"
"context"
"fusenapi/server/product/internal/svc"
"fusenapi/server/product/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type GetProductDetailLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetProductDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetProductDetailLogic {
return &GetProductDetailLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
// 处理进入前逻辑w,r
// func (l *GetProductDetailLogic) BeforeLogic(w http.ResponseWriter, r *http.Request) {
// }
func (l *GetProductDetailLogic) GetProductDetail(req *types.GetProductDetailReq, userinfo *auth.UserInfo) (resp *basic.Response) {
if req.ProductId <= 0 {
return resp.SetStatusWithMessage(basic.CodeRequestParamsErr, "err param:product id is invalid")
}
req.TemplateTag = strings.Trim(req.TemplateTag, " ")
if req.TemplateTag == "" {
return resp.SetStatusWithMessage(basic.CodeRequestParamsErr, "err param:template tag is invalid")
}
//获取产品信息
productInfo, err := l.svcCtx.AllModels.FsProduct.FindOne(l.ctx, req.ProductId)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return resp.SetStatusWithMessage(basic.CodeDbRecordNotFoundErr, "the product is not exists")
}
logx.Error(err)
return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "failed to get product info")
}
if *productInfo.Status != 1 {
return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "the product status is unNormal")
}
if *productInfo.IsShelf != 1 {
return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "the product status is off shelf")
}
if *productInfo.IsDel == 1 {
return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "the product status is deleted")
}
//解析产品单位
var websiteUnit interface{}
if *productInfo.WebsiteUnit != "" {
if err = json.Unmarshal([]byte(*productInfo.WebsiteUnit), &websiteUnit); err != nil {
logx.Error(err)
return resp.SetStatusWithMessage(basic.CodeJsonErr, "failed to parse website unit")
}
}
//获取产品类型
productTag, err := l.svcCtx.AllModels.FsTags.FindOne(l.ctx, *productInfo.Type)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return resp.SetStatusWithMessage(basic.CodeDbRecordNotFoundErr, "the product`s tag is not exists")
}
logx.Error(err)
return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "failed to get product tag info")
}
//获取模板标签颜色选择信息
templateTagColorInfo, err := l.GetTemplateTagColor(req, userinfo)
if err != nil {
return resp.SetStatusWithMessage(basic.CodeServiceErr, err.Error())
}
//获取产品尺寸列表
sizeList, err := l.svcCtx.AllModels.FsProductSize.GetAllByProductIds(l.ctx, []int64{req.ProductId}, "is_hot DESC,sort ASC")
if err != nil {
logx.Error(err)
return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "failed to get size list")
}
//获取模型+配件信息
modelList, err := l.svcCtx.AllModels.FsProductModel3d.GetAllByProductIdTags(l.ctx, req.ProductId, []int64{constants.TAG_MODEL, constants.TAG_PARTS})
if err != nil {
logx.Error(err)
return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "failed to get model list")
}
mapSizeKeyModel := make(map[int64]int) //模型不包含配件sizeId为key
mapFitting := make(map[int64]int) //配件
publicFittingOptionTemplateIds := make([]int64, 0, len(modelList)) //配件配置了公共模板的模板id
notPublicFittingOptionTemplateFittingIds := make([]int64, 0, len(modelList)) //配件没有配置公共模板的配件id
lightIds := make([]int64, 0, len(modelList))
for k, v := range modelList {
switch *v.Tag {
case constants.TAG_MODEL: //模型的
mapSizeKeyModel[*v.SizeId] = k
if *v.Light > 0 {
lightIds = append(lightIds, *v.Light)
}
case constants.TAG_PARTS: //配件的
mapFitting[v.Id] = k
if *v.OptionTemplate > 0 {
publicFittingOptionTemplateIds = append(publicFittingOptionTemplateIds, *v.OptionTemplate)
} else {
notPublicFittingOptionTemplateFittingIds = append(notPublicFittingOptionTemplateFittingIds, v.Id)
}
}
}
//获取没有绑定公共模板的配件贴图
notPublicFittingOptionTemplateList, err := l.svcCtx.AllModels.FsProductTemplateV2.FindAllByFittingIds(l.ctx, notPublicFittingOptionTemplateFittingIds, "id,model_id,material_img")
if err != nil {
logx.Error(err)
return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "failed to get fitting material image list")
}
mapNotPublicFittingOptionTemplateMaterialImage := make(map[int64]string) //model_id为key
for _, v := range notPublicFittingOptionTemplateList {
mapNotPublicFittingOptionTemplateMaterialImage[*v.ModelId] = *v.MaterialImg
}
//获取配件绑定的公共模板列表
publicFittingOptionTemplateList, err := l.svcCtx.AllModels.FsProductTemplateV2.FindAllByIds(l.ctx, publicFittingOptionTemplateIds, "id,model_id,material_img")
if err != nil {
logx.Error(err)
return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "failed to get fitting optional template list")
}
mapPublicFittingOptionTemplate := make(map[int64]string) //模板id为key
for _, v := range publicFittingOptionTemplateList {
mapPublicFittingOptionTemplate[v.Id] = *v.MaterialImg
}
//获取灯光列表
lightList, err := l.svcCtx.AllModels.FsProductModel3dLight.GetAllByIds(l.ctx, lightIds)
if err != nil {
logx.Error(err)
return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "failed to get light list")
}
mapLight := make(map[int64]int)
for k, v := range lightList {
mapLight[v.Id] = k
}
//获取产品模板列表
templateList, err := l.svcCtx.AllModels.FsProductTemplateV2.FindAllByProductIdsTemplateTag(l.ctx, []int64{req.ProductId}, req.TemplateTag, "")
if err != nil {
logx.Error(err)
return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "failed to get template list")
}
mapModelIdKeyTemplate := make(map[int64]int)
for k, v := range templateList {
mapModelIdKeyTemplate[*v.ModelId] = k
}
//记录产品最低价
mapProductMinPrice := make(map[int64]int64)
if err = l.svcCtx.AllModels.FsProductModel3d.GetProductMinPrice(modelList, mapProductMinPrice); err != nil {
logx.Error(err)
return resp.SetStatusWithMessage(basic.CodeServiceErr, "failed to get product min price")
}
//获取默认渲染的尺寸
defaultSize, err := l.getRenderDefaultSize(req.ProductId, req.TemplateTag)
if err != nil {
logx.Error("获取默认尺寸失败:", err.Error())
//return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "failed to get default size")
}
//整理返回
rspSizeList := make([]types.SizeInfo, 0, len(sizeList))
for _, sizeInfo := range sizeList {
var sizeTitle interface{}
if err = json.Unmarshal([]byte(*sizeInfo.Title), &sizeTitle); err != nil {
logx.Error(err)
return resp.SetStatusWithMessage(basic.CodeJsonErr, "failed to parse size title")
}
//尺寸下最低价
minPrice := ""
if price, ok := mapProductMinPrice[*sizeInfo.ProductId]; ok {
minPrice = format.CentitoDollar(price, 3)
}
var modelInfoRsp types.ModelInfo
var TemplateInfoRsp interface{}
var FittingListRsp []types.FittingInfo
if modelIndex, ok := mapSizeKeyModel[sizeInfo.Id]; ok {
modelInfo := modelList[modelIndex]
//模板信息
if templateIndex, ok := mapModelIdKeyTemplate[modelInfo.Id]; ok {
templateInfo := templateList[templateIndex]
//获取开关信息
TemplateInfoRsp = template_switch_info.GetTemplateSwitchInfo(templateInfo.Id, templateInfo.TemplateInfo, *templateInfo.MaterialImg)
}
//赋值id
modelInfoRsp.Id = modelInfo.Id
//模型设计信息
var modelDesignInfo interface{}
if modelInfo.ModelInfo != nil && *modelInfo.ModelInfo != "" {
if err = json.Unmarshal([]byte(*modelInfo.ModelInfo), &modelDesignInfo); err != nil {
logx.Error(err)
return resp.SetStatusWithMessage(basic.CodeJsonErr, "failed to parse model design info")
}
//赋值
modelInfoRsp.ModelDesignInfo = modelDesignInfo
}
//灯光信息
if lightIndex, ok := mapLight[*modelInfo.Light]; ok {
lightInfo := lightList[lightIndex]
var lightDesignInfo interface{}
if lightInfo.Info != nil && *lightInfo.Info != "" {
if err = json.Unmarshal([]byte(*lightInfo.Info), &lightDesignInfo); err != nil {
logx.Error(err)
return resp.SetStatusWithMessage(basic.CodeJsonErr, "failed to parse light design info")
}
//赋值
modelInfoRsp.LightInfo = types.LightInfo{
Id: lightInfo.Id,
LightName: *lightInfo.Name,
LightDesignInfo: lightDesignInfo,
}
}
}
optionalFittingIdsStr := strings.Trim(*modelInfo.PartList, " ")
optionalFittingIdsStr = strings.Trim(optionalFittingIdsStr, ",")
//配件信息
FittingListRsp, err = l.GetModelOptionalFittings(l.ctx, optionalFittingIdsStr, mapFitting, mapPublicFittingOptionTemplate, mapNotPublicFittingOptionTemplateMaterialImage, modelInfo, modelList)
if err != nil {
return resp.SetStatusWithMessage(basic.CodeServiceErr, err.Error())
}
}
isDefaultSize := int64(0)
if sizeInfo.Id == defaultSize {
isDefaultSize = 1
}
rspSizeList = append(rspSizeList, types.SizeInfo{
Id: sizeInfo.Id,
IsDefault: isDefaultSize,
Title: sizeTitle,
Capacity: *sizeInfo.Capacity,
PartsCanDeleted: *sizeInfo.PartsCanDeleted,
IsHot: *sizeInfo.IsHot,
MinPrice: minPrice,
TemplateInfo: TemplateInfoRsp,
ModelInfo: modelInfoRsp,
FittingList: FittingListRsp,
})
}
return resp.SetStatusWithMessage(basic.CodeOK, "success", types.GetProductDetailRsp{
Logo: req.Logo,
TemplateTagColorInfo: templateTagColorInfo,
ProductInfo: types.ProductInfo{
Id: productInfo.Id,
Description: *productInfo.Intro,
ProductType: *productInfo.Type,
ProductTypeName: *productTag.Title,
Title: *productInfo.Title,
IsEnv: *productInfo.IsProtection,
IsMicro: *productInfo.IsMicrowave,
IsCustomization: *productInfo.IsCustomization,
WebsiteUnit: websiteUnit,
},
BaseColors: color_list.GetColor(),
SizeList: rspSizeList,
})
}
// 获取模型可选配件列表
func (l *GetProductDetailLogic) GetModelOptionalFittings(ctx context.Context, optionalFittingIdsStr string, mapFitting map[int64]int, mapPublicFittingOptionTemplate, mapNotPublicFittingOptionTemplateMaterialImage map[int64]string, modelInfo gmodel.FsProductModel3d, modelList []gmodel.FsProductModel3d) (resp []types.FittingInfo, err error) {
if optionalFittingIdsStr == "" {
return
}
optionalFittingIds, err := format.StrSlicToInt64Slice(strings.Split(optionalFittingIdsStr, ","))
if err != nil {
logx.Error(err)
return nil, errors.New("failed to split optional fitting list")
}
resp = make([]types.FittingInfo, 0, len(optionalFittingIds))
//可选配件
for _, optionFittingId := range optionalFittingIds {
fittingIndex, ok := mapFitting[optionFittingId]
if !ok {
continue
}
fittingInfo := modelList[fittingIndex]
var fittingDesignInfo interface{}
if fittingInfo.ModelInfo != nil && *fittingInfo.ModelInfo != "" {
if err = json.Unmarshal([]byte(*fittingInfo.ModelInfo), &fittingDesignInfo); err != nil {
logx.Error(err)
return nil, errors.New("failed to parse fitting design info")
}
}
//是否默认显示配件
isDefault := int64(0)
if optionFittingId == *modelInfo.PartId {
isDefault = 1
}
//配件贴图
FittingMaterialImg := ""
//贴图,如果绑定了公共模板,则获取公共模板的贴图数据(待优化)
if *fittingInfo.OptionTemplate > 0 {
if image, ok := mapPublicFittingOptionTemplate[*fittingInfo.OptionTemplate]; ok {
FittingMaterialImg = image
}
} else { //否则取该配件下的模板贴图
if image, ok := mapNotPublicFittingOptionTemplateMaterialImage[fittingInfo.Id]; ok {
FittingMaterialImg = image
}
}
resp = append(resp, types.FittingInfo{
Id: fittingInfo.Id,
IsHot: *fittingInfo.IsHot,
MaterialImage: FittingMaterialImg,
DesignInfo: fittingDesignInfo,
Price: format.CentitoDollar(*fittingInfo.Price, 3),
Name: *fittingInfo.Name,
IsDefault: isDefault,
})
}
return resp, nil
}
// 获取列表页默认渲染的尺寸(同列表页)
func (l *GetProductDetailLogic) getRenderDefaultSize(productId int64, templateTag string) (sizeId int64, err error) {
//获取模板
productTemplate, err := l.svcCtx.AllModels.FsProductTemplateV2.FindOneCloudRenderByProductIdTemplateTag(l.ctx, productId, templateTag, "sort ASC", "model_id")
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return 0, errors.New("找不到对应开启云渲染模板")
}
logx.Error(err)
return 0, errors.New("获取对应开启云渲染模板失败")
}
//根据模板找到模型
model3d, err := l.svcCtx.AllModels.FsProductModel3d.FindOne(l.ctx, *productTemplate.ModelId, "size_id")
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return 0, errors.New("找不到对应模型")
}
logx.Error(err)
return 0, errors.New("获取对应模型失败")
}
return *model3d.SizeId, nil
}
// 获取对应模板标签颜色信息
func (l *GetProductDetailLogic) GetTemplateTagColor(req *types.GetProductDetailReq, userinfo *auth.UserInfo) (resp types.TemplateTagColorInfo, err error) {
if req.SelectedColorIndex < 0 {
return types.TemplateTagColorInfo{}, errors.New("param selected_color_index is invalid")
}
if req.Logo == "" {
//颜色选择置0
req.SelectedColorIndex = 0
//获取默认profile从中获取logo
profile, err := l.svcCtx.AllModels.FsUserInfo.GetProfile(l.ctx, "logo_selected", 0, 0)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return types.TemplateTagColorInfo{}, errors.New("the default profile info is not exists")
}
logx.Error(err)
return types.TemplateTagColorInfo{}, errors.New("failed to get default profile info for without logo")
}
if profile["logo_url"] != nil && reflect.TypeOf(profile["logo_url"]).String() == "string" {
req.Logo = profile["logo_url"].(string)
} else {
return types.TemplateTagColorInfo{}, errors.New("default profile logo url is not set !!")
}
}
//根据logo查询素材资源
resourceId := s3url_to_s3id.GetS3ResourceIdFormUrl(req.Logo)
if resourceId == "" {
return types.TemplateTagColorInfo{}, errors.New("param logo is invalid")
}
var (
userMaterial *gmodel.FsUserMaterial
templateTagInfo *gmodel.FsProductTemplateTags
)
//获取模板标签信息
templateTagInfo, err = l.svcCtx.AllModels.FsProductTemplateTags.FindOneByTagName(l.ctx, req.TemplateTag)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return types.TemplateTagColorInfo{}, errors.New("the template tag is not exists")
}
logx.Error(err)
return types.TemplateTagColorInfo{}, errors.New("failed to get template tag info")
}
userMaterial, err = l.svcCtx.AllModels.FsUserMaterial.FindOneByLogoResourceId(l.ctx, resourceId)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return types.TemplateTagColorInfo{}, errors.New("the logo is not found")
}
logx.Error(err)
return types.TemplateTagColorInfo{}, errors.New("failed to get user material")
}
if userMaterial.Metadata == nil || len(*userMaterial.Metadata) == 0 {
return types.TemplateTagColorInfo{}, errors.New("the user material is empty")
}
//解析用户素材元数据
var metaData map[string]interface{}
if err = json.Unmarshal(*userMaterial.Metadata, &metaData); err != nil {
logx.Error(err)
return types.TemplateTagColorInfo{}, errors.New("failed to parse user metadata")
}
var mapMaterialTemplateTag map[string][][]string
b, _ := json.Marshal(metaData["template_tag"])
if err = json.Unmarshal(b, &mapMaterialTemplateTag); err != nil {
logx.Error(err)
return types.TemplateTagColorInfo{}, errors.New("invalid format of metadata`s template_tag")
}
colors, ok := mapMaterialTemplateTag[req.TemplateTag]
if !ok {
return types.TemplateTagColorInfo{}, errors.New("the template tag is not found from this logo material`s metadata")
}
if req.SelectedColorIndex >= len(colors) {
return types.TemplateTagColorInfo{}, errors.New("select color index is out of range !!")
}
var templateTagGroups interface{}
if templateTagInfo.Groups != nil && *templateTagInfo.Groups != "" {
if err = json.Unmarshal([]byte(*templateTagInfo.Groups), &templateTagGroups); err != nil {
logx.Error(err)
return types.TemplateTagColorInfo{}, errors.New("failed to parse template tag`s groups info")
}
}
return types.TemplateTagColorInfo{
Colors: colors,
SelectedColorIndex: req.SelectedColorIndex,
TemplateTagGroups: templateTagGroups,
}, nil
}