fusenapi/server/product/internal/logic/getproductinfologic.go
laodaming 3a6ab7a666 fix
2023-07-14 15:57:27 +08:00

535 lines
18 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 (
"context"
"encoding/json"
"errors"
"fmt"
"fusenapi/constants"
"fusenapi/model/gmodel"
"fusenapi/utils/auth"
"fusenapi/utils/basic"
"fusenapi/utils/color_list"
"fusenapi/utils/encryption_decryption"
"fusenapi/utils/format"
"fusenapi/utils/image"
"fusenapi/utils/step_price"
"gorm.io/gorm"
"strconv"
"strings"
"fusenapi/server/product/internal/svc"
"fusenapi/server/product/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type GetProductInfoLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetProductInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetProductInfoLogic {
return &GetProductInfoLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetProductInfoLogic) GetProductInfo(req *types.GetProductInfoReq, userinfo *auth.UserInfo) (resp *basic.Response) {
//获取产品信息
productInfo, err := l.svcCtx.AllModels.FsProduct.FindOneBySn(l.ctx, req.Pid)
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 req.Size > 0 {
req.Size = image.GetCurrentSize(req.Size)
}
materialIdSlice, err := format.StrSlicToInt64Slice(strings.Split(*productInfo.MaterialIds, ","))
if err != nil {
logx.Error(err)
return resp.SetStatusWithMessage(basic.CodeServiceErr, "failed to parse product material Id list")
}
//材料列表
materials := make([]types.MaterialItem, 0, len(materialIdSlice))
for _, v := range materialIdSlice {
if title, ok := constants.MAP_MATERIAL[v]; ok {
materials = append(materials, types.MaterialItem{
Id: v,
Title: title,
})
}
}
//尺寸列表
sizeList, err := l.svcCtx.AllModels.FsProductSize.GetAllByStatus(l.ctx, int64(constants.FAQ_STATUS_ON), 1, "id,title,capacity,cover,sort,parts_can_deleted")
if err != nil {
logx.Error(err)
return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "failed to get product size list")
}
sizeIds := make([]int64, 0, len(sizeList))
for _, v := range sizeList {
sizeIds = append(sizeIds, v.Id)
}
//获取产品标签
tagInfo, err := l.svcCtx.AllModels.FsTags.FindOne(l.ctx, *productInfo.Type)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return resp.SetStatusWithMessage(basic.CodeDbRecordNotFoundErr, "tag info is not exists")
}
logx.Error(err)
return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "failed to get tag info")
}
typeName := *tagInfo.Title
//获取该尺寸下的模型数据
model3dList, err := l.svcCtx.AllModels.FsProductModel3d.GetAllBySizeIdsTag(l.ctx, sizeIds, constants.TAG_MODEL)
if err != nil {
logx.Error(err)
return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "failed to get product 3d model list")
}
model3dIds := make([]int64, 0, len(model3dList))
mapModel3dWithSizeIdIndex := make(map[int64]gmodel.FsProductModel3d) //sizeid为key
for _, v := range model3dList {
model3dIds = append(model3dIds, v.Id)
mapModel3dWithSizeIdIndex[*v.SizeId] = v
}
//通过产品id和模型id获取模板信息
productTemplateList, err := l.svcCtx.AllModels.FsProductTemplateV2.FindAllByProductIdModelIds(l.ctx, model3dIds, productInfo.Id)
if err != nil {
logx.Error(err)
return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "failed to get product template list")
}
//获取模板包含的model_id
mapTemplateModelId := make(map[int64]struct{})
tagIds := make([]int64, 0, len(productTemplateList))
for _, v := range productTemplateList {
mapTemplateModelId[*v.ModelId] = struct{}{}
if v.Tag != nil && *v.Tag != "" {
tagId, err := strconv.ParseInt(*v.Tag, 10, 64)
if err != nil {
logx.Error(err)
return resp.SetStatusWithMessage(basic.CodeServiceErr, "tag is not a number")
}
tagIds = append(tagIds, tagId)
}
}
//过滤没有模板的尺寸数据
sizeListRsp := make([]types.SizeItem, 0, len(sizeList))
for _, v := range sizeList {
model3dInfo, ok := mapModel3dWithSizeIdIndex[v.Id]
if !ok {
continue
}
if _, ok = mapTemplateModelId[model3dInfo.Id]; !ok {
continue
}
var title types.SizeTitle
if err = json.Unmarshal([]byte(*v.Title), &title); err != nil {
logx.Error(err)
return resp.SetStatusWithMessage(basic.CodeJsonErr, "failed to parse size info`s title")
}
var modelInfo map[string]interface{}
if model3dInfo.ModelInfo != nil && *model3dInfo.ModelInfo != "" {
if err = json.Unmarshal([]byte(*model3dInfo.ModelInfo), &modelInfo); err != nil {
logx.Error(err)
return resp.SetStatusWithMessage(basic.CodeJsonErr, "failed to parse model info")
}
}
cover := ""
if modelInfo["cover"] != nil && modelInfo["cover"].(string) != "" {
cover = modelInfo["cover"].(string)
if req.Size >= 200 {
coverArr := strings.Split(modelInfo["cover"].(string), ".")
cover = fmt.Sprintf("%s_%d.%s", coverArr[0], req.Size, coverArr[1])
}
}
sizeListRsp = append(sizeListRsp, types.SizeItem{
Id: v.Id,
Title: title,
Capacity: *v.Capacity,
Cover: cover,
Sort: *v.Sort,
PartsCanDeleted: *v.PartsCanDeleted > 0,
})
}
//获取标签列表
tagList, err := l.svcCtx.AllModels.FsTags.GetAllByIdsWithoutStatus(l.ctx, tagIds)
if err != nil {
logx.Error(err)
return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "failed to get tag list")
}
mapTag := make(map[string]gmodel.FsTags)
for _, v := range tagList {
mapTag[fmt.Sprintf("%d", v.Id)] = v
}
//获取全部模型信息
allModel3dList, err := l.svcCtx.AllModels.FsProductModel3d.GetAll(l.ctx)
if err != nil {
logx.Error(err)
return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "failed to get all 3d model list")
}
mapAllmodel3d := make(map[int64]gmodel.FsProductModel3d)
optionTemplateIds := make([]int64, 0, len(allModel3dList))
lightIds := make([]int64, 0, len(allModel3dList))
for _, v := range allModel3dList {
mapAllmodel3d[v.Id] = v
optionTemplateIds = append(optionTemplateIds, *v.OptionTemplate)
lightIds = append(lightIds, *v.Light)
}
//获取公共模板信息
optionTemplateList, err := l.svcCtx.AllModels.FsProductTemplateV2.FindAllByIdsWithoutStatus(l.ctx, optionTemplateIds, "id,material_img")
if err != nil {
logx.Error(err)
return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "failed to get option template list")
}
mapOptionTemplate := make(map[int64]gmodel.FsProductTemplateV2)
for _, v := range optionTemplateList {
mapOptionTemplate[v.Id] = v
}
//获取灯光信息
lightList, err := l.svcCtx.AllModels.FsProductModel3dLight.GetAllByIdsWithoutStatus(l.ctx, lightIds)
if err != nil {
logx.Error(err)
return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "failed to get light list")
}
mapLight := make(map[int64]gmodel.FsProductModel3dLight)
for _, v := range lightList {
mapLight[v.Id] = v
}
//材料尺寸模板
mapMaterialSizeTmp := make(map[string][]interface{})
//循环阶梯价计算
type MaterialSizePrice struct {
Items []interface{} `json:"items"`
MinPrice float64 `json:"min_price"`
MaxPrice float64 `json:"max_price"`
}
mapMaterialSizePrice := make(map[string]*MaterialSizePrice)
//循环处理组装模板信息
for _, tmp := range productTemplateList {
model3dInfo, ok := mapAllmodel3d[*tmp.ModelId]
if !ok {
continue
}
//如果是配件信息就跳过,不返回
if *model3dInfo.Tag == constants.TAG_PARTS {
continue
}
//未编辑模板信息的数据跳过
if tmp.TemplateInfo == nil || *tmp.TemplateInfo == "" {
continue
}
//解码template info
var templateInfoRsp map[string]interface{}
if tmp.TemplateInfo != nil && *tmp.TemplateInfo != "" {
if err = json.Unmarshal([]byte(*tmp.TemplateInfo), &templateInfoRsp); err != nil {
logx.Error(err)
return resp.SetStatusWithMessage(basic.CodeJsonErr, "failed to parse template info")
}
}
if templateInfoRsp["cover"] != nil && templateInfoRsp["cover"].(string) != "" {
cover := templateInfoRsp["cover"].(string)
if req.Size >= 200 {
coverArr := strings.Split(templateInfoRsp["cover"].(string), ".")
cover = fmt.Sprintf("%s_%d.%s", coverArr[0], req.Size, coverArr[1])
}
templateInfoRsp["cover"] = cover
delete(templateInfoRsp, "isPublic")
delete(templateInfoRsp, "name")
}
//解码模型数据
var modelInfoRsp map[string]interface{}
if model3dInfo.ModelInfo != nil && *model3dInfo.ModelInfo != "" {
if err = json.Unmarshal([]byte(*model3dInfo.ModelInfo), &modelInfoRsp); err != nil {
logx.Error(err)
return resp.SetStatusWithMessage(basic.CodeJsonErr, "failed to parse model info")
}
}
modelInfoRsp["id"] = model3dInfo.Id
//解码灯光数据
var lightInfoRsp interface{}
lightInfo, ok := mapLight[*model3dInfo.Light]
if ok && lightInfo.Info != nil && *lightInfo.Info != "" {
if err = json.Unmarshal([]byte(*lightInfo.Info), &lightInfoRsp); err != nil {
logx.Error(err)
return resp.SetStatusWithMessage(basic.CodeJsonErr, "failed to parse light info")
}
}
//配件备选项
modelPartIds, err := format.StrSlicToInt64Slice(strings.Split(*model3dInfo.PartList, ","))
if err != nil {
logx.Error(err)
return resp.SetStatusWithMessage(basic.CodeServiceErr, "failed to parse 3d model`s part_list")
}
partListRsp := make([]interface{}, 0, len(modelPartIds))
for _, partId := range modelPartIds {
//判断配件信息是否正常
temModelInfo, ok := mapAllmodel3d[partId]
if !ok || *temModelInfo.Status != 1 {
continue
}
var thisInfo map[string]interface{}
temBytes, _ := json.Marshal(temModelInfo)
_ = json.Unmarshal(temBytes, &thisInfo)
thisInfo["material_img"] = ""
if *temModelInfo.OptionTemplate != 0 {
if optionTemplateInfo, ok := mapOptionTemplate[*temModelInfo.OptionTemplate]; ok {
thisInfo["material_img"] = *optionTemplateInfo.MaterialImg
}
} else {
tmpv2, err := l.svcCtx.AllModels.FsProductTemplateV2.FindOneByModelId(l.ctx, partId)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
} else {
logx.Error(err)
return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "failed to get product template")
}
}
thisInfo["material_img"] = *tmpv2.MaterialImg
}
partListRsp = append(partListRsp, thisInfo)
}
tagName := ""
if tagData, ok := mapTag[*tmp.Tag]; ok {
tagName = *tagData.Title
}
//按照材质和尺寸来存放模板信息
mapMaterialSizeTmpKey := l.getMapMaterialSizeTmpKey(*productInfo.MaterialIds, *model3dInfo.SizeId)
mapMaterialSizeTmp[mapMaterialSizeTmpKey] = append(mapMaterialSizeTmp[mapMaterialSizeTmpKey], map[string]interface{}{
"id": tmp.Id,
"title": *tmp.Title,
"templateData": templateInfoRsp,
"modelData": modelInfoRsp,
"lightData": lightInfoRsp,
"partList": partListRsp,
"tag_name": tagName,
})
}
//产品价格查询
productPriceList, err := l.svcCtx.AllModels.FsProductPrice.GetAllByProductIdStatus(l.ctx, productInfo.Id, 1)
if err != nil {
logx.Error(err)
return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "failed to get product price list")
}
for _, priceItem := range productPriceList {
stepNumSlice, err := format.StrSlicToIntSlice(strings.Split(*priceItem.StepNum, ","))
if err != nil {
logx.Error(err)
return resp.SetStatusWithMessage(basic.CodeServiceErr, "split step num err")
}
stepPriceSlice, err := format.StrSlicToIntSlice(strings.Split(*priceItem.StepPrice, ","))
if err != nil {
logx.Error(err)
return resp.SetStatusWithMessage(basic.CodeServiceErr, "split step price err")
}
lenStepNum := len(stepNumSlice)
lenStepPrice := len(stepPriceSlice)
if lenStepNum == 0 {
return resp.SetStatusWithMessage(basic.CodeServiceErr, "count of step num is empty")
}
for *priceItem.MinBuyNum < int64(stepNumSlice[lenStepNum-1]+5) {
price := step_price.GetCentStepPrice(int(*priceItem.MinBuyNum), stepNumSlice, stepPriceSlice)
mapMaterialSizePriceKey := l.getMapMaterialSizePriceKey(*priceItem.MaterialId, *priceItem.SizeId)
minPriceStr := fmt.Sprintf("%.2f", float64(stepPriceSlice[lenStepPrice-1])/100)
minPrice, _ := strconv.ParseFloat(minPriceStr, 64)
maxPriceStr := fmt.Sprintf("%.2f", float64(stepPriceSlice[0])/100)
maxPrice, _ := strconv.ParseFloat(maxPriceStr, 64)
if _, ok := mapMaterialSizePrice[mapMaterialSizePriceKey]; ok {
mapMaterialSizePrice[mapMaterialSizePriceKey].Items = append(mapMaterialSizePrice[mapMaterialSizePriceKey].Items, map[string]interface{}{
"num": *priceItem.MinBuyNum,
"total_num": *priceItem.MinBuyNum * (*priceItem.EachBoxNum),
"price": price,
})
mapMaterialSizePrice[mapMaterialSizePriceKey].MinPrice = minPrice
mapMaterialSizePrice[mapMaterialSizePriceKey].MaxPrice = maxPrice
} else {
items := map[string]interface{}{
"num": *priceItem.MinBuyNum,
"total_num": *priceItem.MinBuyNum * (*priceItem.EachBoxNum),
"price": price,
}
mapMaterialSizePrice[mapMaterialSizePriceKey] = &MaterialSizePrice{
Items: []interface{}{items},
MinPrice: minPrice,
MaxPrice: maxPrice,
}
}
*priceItem.MinBuyNum++
}
}
isLowRendering := false
isRemoveBg := false
var lastDesign interface{}
if userinfo.UserId != 0 {
//获取用户信息
user, err := l.svcCtx.AllModels.FsUser.FindUserById(l.ctx, userinfo.UserId)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return resp.SetStatusWithMessage(basic.CodeDbRecordNotFoundErr, "user info not found")
}
logx.Error(err)
return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "failed to get user info")
}
isLowRendering = *user.IsLowRendering > 0
isRemoveBg = *user.IsRemoveBg > 0
lastDesign = l.getLastDesign(user)
}
var renderDesign interface{}
if req.HaveCloudRendering == true {
renderDesign = l.getRenderDesign(req.ClientNo)
}
//查询是否是加密的(不太合理)
encryptWebsetting, err := l.svcCtx.AllModels.FsWebSet.FindValueByKey(l.ctx, "is_encrypt")
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return resp.SetStatusWithMessage(basic.CodeDbRecordNotFoundErr, "web setting is_encrypt is not exists")
}
logx.Error(err)
return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "failed to get web setting")
}
//不加密
rspData := types.GetProductInfoRsp{
Id: productInfo.Id,
Type: *productInfo.Type,
Title: *productInfo.Title,
IsEnv: *productInfo.IsProtection,
IsMicro: *productInfo.IsMicrowave,
TypeName: typeName,
IsLowRendering: isLowRendering,
IsRemoveBg: isRemoveBg,
Materials: materials,
Sizes: sizeListRsp,
Templates: mapMaterialSizeTmp,
Prices: mapMaterialSizePrice,
LastDesign: lastDesign,
RenderDesign: renderDesign,
Colors: color_list.GetColor(),
}
if encryptWebsetting.Value == nil || *encryptWebsetting.Value == "0" {
return resp.SetStatusWithMessage(basic.CodeOK, "success", rspData)
}
bytesData, _ := json.Marshal(rspData)
enData, err := encryption_decryption.CBCEncrypt(string(bytesData))
if err != nil {
logx.Error(err)
return resp.SetStatusWithMessage(basic.CodeServiceErr, "failed to encryption response data")
}
return resp.SetStatusWithMessage(basic.CodeOK, "success", enData)
}
// 获取渲染设计
func (l *GetProductInfoLogic) getRenderDesign(clientNo string) interface{} {
if clientNo == "" {
return nil
}
renderDesign, err := l.svcCtx.AllModels.FsProductRenderDesign.FindOneRenderDesignByParams(l.ctx, gmodel.FindOneRenderDesignByParamsReq{
ClientNo: &clientNo,
Fields: "id,info,material_id,optional_id,size_id,template_id",
OrderBy: "`id` DESC",
})
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil
}
logx.Error(err)
return nil
}
var info interface{}
if renderDesign.Info != nil && *renderDesign.Info != "" {
if err = json.Unmarshal([]byte(*renderDesign.Info), &info); err != nil {
logx.Error(err)
return nil
}
}
return map[string]interface{}{
"id": renderDesign.Id,
"info": info,
"material_id": *renderDesign.MaterialId,
"optional_id": *renderDesign.OptionalId,
"size_id": *renderDesign.SizeId,
"template_id": *renderDesign.TemplateId,
}
}
// 获取用户最新设计
func (l *GetProductInfoLogic) getLastDesign(userInfo gmodel.FsUser) interface{} {
//查询用户最近下单成功的数据
orderInfo, err := l.svcCtx.AllModels.FsOrder.FindLastSuccessOneOrder(l.ctx, userInfo.Id, int64(constants.STATUS_NEW_NOT_PAY))
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil
}
logx.Error(err)
return nil
}
//获取该订单相关设计信息
orderDetail, err := l.svcCtx.AllModels.FsOrderDetail.GetOneOrderDetailByOrderId(l.ctx, orderInfo.Id)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil
}
logx.Error(err)
return nil
}
//获取设计模板详情便于获得design_id
orderDetailTemplate, err := l.svcCtx.AllModels.FsOrderDetailTemplate.FindOne(l.ctx, *orderDetail.OrderDetailTemplateId)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil
}
logx.Error(err)
return nil
}
//若没打开了个性化渲染按钮或者最后一次设计不存在,则不返回该设计相关数据
if *userInfo.IsOpenRender != 1 || *orderDetailTemplate.DesignId <= 0 {
return nil
}
//获取设计数据
productDesign, err := l.svcCtx.AllModels.FsProductDesign.FindOne(l.ctx, *orderDetailTemplate.DesignId, userInfo.Id)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil
}
logx.Error(err)
return nil
}
var info interface{}
if productDesign.Info != nil && *productDesign.Info != "" {
if err := json.Unmarshal([]byte(*productDesign.Info), &info); err != nil {
logx.Error(err)
return nil
}
}
var logoColor interface{}
if productDesign.LogoColor != nil && *productDesign.LogoColor != "" {
if err := json.Unmarshal([]byte(*productDesign.LogoColor), &logoColor); err != nil {
logx.Error(err)
return nil
}
}
return map[string]interface{}{
"id": productDesign.Id,
"info": info,
"logo_color": logoColor,
"material_id": *productDesign.MaterialId,
"optional_id": *productDesign.OptionalId,
"size_id": *productDesign.SizeId,
}
}
// 获取按照材料跟尺寸分类的模板map key
func (l *GetProductInfoLogic) getMapMaterialSizeTmpKey(materialIds string, sizeId int64) string {
return fmt.Sprintf("%s_%d", materialIds, sizeId)
}
// 获取按照材料跟尺寸分类的价格map key
func (l *GetProductInfoLogic) getMapMaterialSizePriceKey(materialId int64, sizeId int64) string {
return fmt.Sprintf("%d_%d", materialId, sizeId)
}