From 0e72027ff3f9e6b267a69228e006a0345b10c8d8 Mon Sep 17 00:00:00 2001 From: laodaming <11058467+laudamine@user.noreply.gitee.com> Date: Thu, 20 Jul 2023 17:35:13 +0800 Subject: [PATCH] fix --- constants/recommend_product.go | 3 + model/gmodel/fs_product_recommend_gen.go | 2 +- model/gmodel/fs_product_recommend_logic.go | 24 ++- .../homepagerecommandproductlisthandler.go | 76 ++++++++ server/product/internal/handler/routes.go | 15 +- .../homepagerecommandproductlistlogic.go | 178 ++++++++++++++++++ server/product/internal/types/types.go | 19 ++ server_api/product.api | 21 +++ 8 files changed, 319 insertions(+), 19 deletions(-) create mode 100644 server/product/internal/handler/homepagerecommandproductlisthandler.go create mode 100644 server/product/internal/logic/homepagerecommandproductlistlogic.go diff --git a/constants/recommend_product.go b/constants/recommend_product.go index aa1be1c9..68717ba3 100644 --- a/constants/recommend_product.go +++ b/constants/recommend_product.go @@ -4,3 +4,6 @@ type recommend_product int64 // 产品详情页推荐产品 const PRODUCT_DETAIL_RECOMMEND_CATEGORY recommend_product = 1 + +// 主页推荐产品 +const HOME_PAGE_RECOMMEND_CATEGORY recommend_product = 1 diff --git a/model/gmodel/fs_product_recommend_gen.go b/model/gmodel/fs_product_recommend_gen.go index b3dba788..3c7c6363 100644 --- a/model/gmodel/fs_product_recommend_gen.go +++ b/model/gmodel/fs_product_recommend_gen.go @@ -9,7 +9,7 @@ type FsProductRecommend struct { Id int64 `gorm:"primary_key;default:0;auto_increment;" json:"id"` // ProductId *int64 `gorm:"unique_key;default:0;" json:"product_id"` // 产品ID Status *int64 `gorm:"default:1;" json:"status"` // 状态 1正常 0不正常 - Category *int64 `gorm:"default:1;" json:"category"` // 推荐类别1:详情推荐产品 + Category *int64 `gorm:"default:1;" json:"category"` // 推荐类别1:详情推荐 2:列表页推荐 Ctime *int64 `gorm:"default:0;" json:"ctime"` // 添加时间 } type FsProductRecommendModel struct { diff --git a/model/gmodel/fs_product_recommend_logic.go b/model/gmodel/fs_product_recommend_logic.go index c3a10a54..b3e44c6a 100644 --- a/model/gmodel/fs_product_recommend_logic.go +++ b/model/gmodel/fs_product_recommend_logic.go @@ -10,21 +10,17 @@ type GetRecommendProductListReq struct { Ctx context.Context Page int Limit int - OrderBy string - Category int64 - Status *int64 + Category int64 // 0是全部 } -func (r *FsProductRecommendModel) GetRecommendProductList(req GetRecommendProductListReq) (resp []FsProductRecommend, total int64, err error) { - db := r.db.WithContext(req.Ctx).Model(&FsProductRecommend{}) - if req.Status != nil { - db = db.Where("`status` = ?", *req.Status) - } +func (r *FsProductRecommendModel) GetRecommendProductList(req GetRecommendProductListReq) (resp []FsProduct, total int64, err error) { + db := r.db.WithContext(req.Ctx).Select("p.*"). + Table("fs_product_recommend as r"). + Joins("inner join fs_product as p on r.product_id = p.id"). + Where("r.status = ? ", 1). + Where("and p.is_shelf = ? and p.is_del = ? and p.status = ?", 1, 0, 1) if req.Category != 0 { - db = db.Where("`category` = ?", req.Category) - } - if req.OrderBy != "" { - db = db.Order(req.OrderBy) + db = db.Where("r.category = ?", req.Category) } if err = db.Limit(1).Count(&total).Error; err != nil { return nil, 0, err @@ -34,7 +30,9 @@ func (r *FsProductRecommendModel) GetRecommendProductList(req GetRecommendProduc return resp, total, err } func (r *FsProductRecommendModel) GetIgnoreRandomRecommendProductList(ctx context.Context, limit int, category int64, idNotInt []int64) (resp []FsProductRecommend, err error) { - err = r.db.WithContext(ctx).Model(&FsProductRecommend{}).Where("`product_id` not in(?) and `category` = ?", idNotInt, category).Order("RAND()").Limit(limit).Find(&resp).Error + err = r.db.WithContext(ctx).Model(&FsProductRecommend{}). + Where("`product_id` not in(?) and `category` = ? and `status` = ?", idNotInt, category, 1). + Order("RAND()").Limit(limit).Find(&resp).Error return resp, err } func (r *FsProductRecommendModel) CreateOrUpdate(ctx context.Context, productId int64, category int64, data *FsProductRecommend) error { diff --git a/server/product/internal/handler/homepagerecommandproductlisthandler.go b/server/product/internal/handler/homepagerecommandproductlisthandler.go new file mode 100644 index 00000000..448711d0 --- /dev/null +++ b/server/product/internal/handler/homepagerecommandproductlisthandler.go @@ -0,0 +1,76 @@ +package handler + +import ( + "errors" + "fusenapi/server/product/internal/logic" + "fusenapi/server/product/internal/types" + "fusenapi/utils/auth" + "fusenapi/utils/basic" + "github.com/zeromicro/go-zero/core/logx" + "github.com/zeromicro/go-zero/rest/httpx" + "net/http" + + "fusenapi/server/product/internal/svc" +) + +func HomePageRecommandProductListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + + var ( + // 定义错误变量 + err error + // 定义用户信息变量 + userinfo *auth.UserInfo + ) + // 解析JWT token,并对空用户进行判断 + claims, err := svcCtx.ParseJwtToken(r) + // 如果解析JWT token出错,则返回未授权的JSON响应并记录错误消息 + if err != nil { + httpx.OkJsonCtx(r.Context(), w, &basic.Response{ + Code: 401, // 返回401状态码,表示未授权 + Message: "unauthorized", // 返回未授权信息 + }) + logx.Info("unauthorized:", err.Error()) // 记录错误日志 + return + } + + if claims != nil { + // 从token中获取对应的用户信息 + userinfo, err = auth.GetUserInfoFormMapClaims(claims) + // 如果获取用户信息出错,则返回未授权的JSON响应并记录错误消息 + if err != nil { + httpx.OkJsonCtx(r.Context(), w, &basic.Response{ + Code: 401, + Message: "unauthorized", + }) + logx.Info("unauthorized:", err.Error()) + return + } + } else { + // 如果claims为nil,则认为用户身份为白板用户 + userinfo = &auth.UserInfo{UserId: 0, GuestId: 0} + } + + var req types.HomePageRecommandProductListReq + // 如果端点有请求结构体,则使用httpx.Parse方法从HTTP请求体中解析请求数据 + if err := httpx.Parse(r, &req); err != nil { + httpx.OkJsonCtx(r.Context(), w, &basic.Response{ + Code: 510, + Message: "parameter error", + }) + logx.Info(err) + return + } + // 创建一个业务逻辑层实例 + l := logic.NewHomePageRecommandProductListLogic(r.Context(), svcCtx) + resp := l.HomePageRecommandProductList(&req, userinfo) + // 如果响应不为nil,则使用httpx.OkJsonCtx方法返回JSON响应; + if resp != nil { + httpx.OkJsonCtx(r.Context(), w, resp) + } else { + err := errors.New("server logic is error, resp must not be nil") + httpx.ErrorCtx(r.Context(), w, err) + logx.Error(err) + } + } +} diff --git a/server/product/internal/handler/routes.go b/server/product/internal/handler/routes.go index 669d7cef..fc5640a7 100644 --- a/server/product/internal/handler/routes.go +++ b/server/product/internal/handler/routes.go @@ -52,11 +52,6 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { Path: "/api/product/other-list", Handler: OtherProductListHandler(serverCtx), }, - { - Method: http.MethodGet, - Path: "/api/product/recommand", - Handler: GetRecommandProductListHandler(serverCtx), - }, { Method: http.MethodGet, Path: "/api/product/tag_product_list", @@ -107,6 +102,16 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { Path: "/api/product/get_last_product_design", Handler: GetLastProductDesignHandler(serverCtx), }, + { + Method: http.MethodGet, + Path: "/api/product/recommand", + Handler: GetRecommandProductListHandler(serverCtx), + }, + { + Method: http.MethodGet, + Path: "/api/product/home_page_recommand", + Handler: HomePageRecommandProductListHandler(serverCtx), + }, }, ) } diff --git a/server/product/internal/logic/homepagerecommandproductlistlogic.go b/server/product/internal/logic/homepagerecommandproductlistlogic.go new file mode 100644 index 00000000..c90c4aff --- /dev/null +++ b/server/product/internal/logic/homepagerecommandproductlistlogic.go @@ -0,0 +1,178 @@ +package logic + +import ( + "errors" + "fusenapi/constants" + "fusenapi/model/gmodel" + "fusenapi/utils/auth" + "fusenapi/utils/basic" + "fusenapi/utils/format" + "fusenapi/utils/image" + "gorm.io/gorm" + "sort" + "strings" + + "context" + + "fusenapi/server/product/internal/svc" + "fusenapi/server/product/internal/types" + + "github.com/zeromicro/go-zero/core/logx" +) + +type HomePageRecommandProductListLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func NewHomePageRecommandProductListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *HomePageRecommandProductListLogic { + return &HomePageRecommandProductListLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *HomePageRecommandProductListLogic) HomePageRecommandProductList(req *types.HomePageRecommandProductListReq, userinfo *auth.UserInfo) (resp *basic.Response) { + //查询用户信息(不用判断存在) + user, err := l.svcCtx.AllModels.FsUser.FindUserById(l.ctx, userinfo.UserId) + if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { + logx.Error(err) + return resp.SetStatusWithMessage(basic.CodeServiceErr, "get user info err") + } + var ( + recommendProductList []gmodel.FsProduct //产品列表(select 字段需要看查询的地方) + productOptionalPartList []gmodel.GetGroupPartListByProductIdsRsp //产品配件列表 + mapProductHaveOptionFitting = make(map[int64]struct{}) + productPriceList []gmodel.GetPriceListByProductIdsRsp //产品价格列表(select 字段需要看查询的地方) + mapProductMinPrice = make(map[int64]int64) //产品最小价格map + productTemplatesV2 []gmodel.FsProductTemplateV2 //产品模板列表(select 字段需要看查询的地方) + productSizeCountList []gmodel.CountProductSizeByStatusRsp //产品尺寸数量列表(select 字段需要看查询的地方) + mapProductSizeCount = make(map[int64]int64) //产品尺寸数量map + mapProductTemplate = make(map[int64]struct{}) //产品模板map + ) + //获取列表推荐产品 + recommendProductList, _, err = l.svcCtx.AllModels.FsProductRecommend.GetRecommendProductList(gmodel.GetRecommendProductListReq{ + Ctx: l.ctx, + Page: 1, + Limit: 500, //设置最大500 + Category: int64(constants.HOME_PAGE_RECOMMEND_CATEGORY), + }) + if len(recommendProductList) == 0 { + return resp.SetStatusWithMessage(basic.CodeOK, "success", []interface{}{}) + } + productIds := make([]int64, 0, len(recommendProductList)) + for _, product := range recommendProductList { + productIds = append(productIds, product.Id) + } + //获取商品可选配件 + productOptionalPartList, err = l.svcCtx.AllModels.FsProductModel3d.GetGroupPartListByProductIds(l.ctx, productIds) + if err != nil { + logx.Error(err) + return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "failed to get product part list") + } + //存储有配件的map + for _, partList := range productOptionalPartList { + partList.PartList = strings.Trim(partList.PartList, " ") + partList.PartList = strings.Trim(partList.PartList, ",") + if partList.PartList == "" { + continue + } + mapProductHaveOptionFitting[partList.ProductId] = struct{}{} + } + //获取产品价格列表 + productPriceList, err = l.svcCtx.AllModels.FsProductPrice.GetSimplePriceListByProductIds(l.ctx, productIds) + if err != nil { + logx.Error(err) + return resp.SetStatusWithMessage(basic.CodeServiceErr, "failed to get product min price list") + } + //存储产品最小价格 + for _, v := range productPriceList { + priceStrSlic := strings.Split(v.Price, ",") + priceSlice, err := format.StrSlicToIntSlice(priceStrSlic) + if err != nil { + logx.Error(err) + return resp.SetStatusWithMessage(basic.CodeServiceErr, err.Error()) + } + if len(priceSlice) == 0 { + continue + } + sort.Ints(priceSlice) + if min, ok := mapProductMinPrice[v.ProductId]; ok { + if min > int64(priceSlice[0]) { + mapProductMinPrice[v.ProductId] = int64(priceSlice[0]) + } + } else { + mapProductMinPrice[v.ProductId] = int64(priceSlice[0]) + } + } + //获取模板(只是获取产品product_id) + productTemplatesV2, err = l.svcCtx.AllModels.FsProductTemplateV2.FindAllByProductIds(l.ctx, productIds, "product_id") + if err != nil { + logx.Error(err) + return resp.SetStatusWithMessage(basic.CodeServiceErr, "get product template_v2 err") + } + for _, v := range productTemplatesV2 { + mapProductTemplate[*v.ProductId] = struct{}{} + } + //获取产品尺寸数量 + productSizeCountList, err = l.svcCtx.AllModels.FsProductSize.GetGroupProductSizeByStatus(l.ctx, productIds, 1) + if err != nil { + logx.Error(err) + return resp.SetStatusWithMessage(basic.CodeServiceErr, "get product size count err") + } + for _, v := range productSizeCountList { + mapProductSizeCount[v.ProductId] = v.Num + } + //组装返回 + listRsp := make([]types.HomePageRecommandProductListRsp, 0, len(recommendProductList)) + for _, productInfo := range recommendProductList { + minPrice, ok := mapProductMinPrice[productInfo.Id] + _, tmpOk := mapProductTemplate[productInfo.Id] + //无最小价格则不显示 || 没有模板也不显示 + if !ok || !tmpOk { + continue + } + sizeNum := int64(0) + if mapSizeNum, ok := mapProductSizeCount[productInfo.Id]; ok { + sizeNum = mapSizeNum + } + //有无可选配件 + haveOptionalFitting := false + if _, ok = mapProductHaveOptionFitting[productInfo.Id]; ok { + haveOptionalFitting = true + } + item := types.HomePageRecommandProductListRsp{ + ProductId: productInfo.Id, + Sn: *productInfo.Sn, + Title: *productInfo.Title, + Intro: *productInfo.Intro, + IsEnv: *productInfo.IsProtection, + IsMicro: *productInfo.IsMicrowave, + SizeNum: uint32(sizeNum), + MiniPrice: minPrice, + HaveOptionalFitting: haveOptionalFitting, + } + //千人千面处理 + r := image.ThousandFaceImageFormatReq{ + Size: int(req.Size), + IsThousandFace: 0, + Cover: *productInfo.Cover, + CoverImg: *productInfo.CoverImg, + CoverDefault: *productInfo.CoverImg, + ProductId: productInfo.Id, + UserId: user.Id, + } + if user.Id != 0 { + r.IsThousandFace = int(*user.IsThousandFace) + } + image.ThousandFaceImageFormat(&r) + item.Cover = r.Cover + item.CoverImg = r.CoverImg + item.CoverDefault = r.CoverDefault + //加入分类产品切片 + listRsp = append(listRsp, item) + } + return resp.SetStatusWithMessage(basic.CodeOK, "success", listRsp) +} diff --git a/server/product/internal/types/types.go b/server/product/internal/types/types.go index 073f6a3e..75f07e6c 100644 --- a/server/product/internal/types/types.go +++ b/server/product/internal/types/types.go @@ -378,6 +378,25 @@ type GetLastProductDesignRsp struct { Info interface{} `json:"info"` } +type HomePageRecommandProductListReq struct { + Size uint32 `form:"size"` +} + +type HomePageRecommandProductListRsp struct { + ProductId int64 `json:"product_id"` + Sn string `json:"sn"` + Title string `json:"title"` + Cover string `json:"cover"` + Intro string `json:"intro"` + CoverImg string `json:"cover_img"` + IsEnv int64 `json:"is_env"` + IsMicro int64 `json:"is_micro"` + SizeNum uint32 `json:"size_num"` + MiniPrice int64 `json:"mini_price"` + CoverDefault string `json:"cover_default"` + HaveOptionalFitting bool `json:"have_optional_fitting"` +} + type Request struct { } diff --git a/server_api/product.api b/server_api/product.api index 4729fd54..dc6beb8a 100644 --- a/server_api/product.api +++ b/server_api/product.api @@ -68,6 +68,9 @@ service product { //获取详情页推荐产品列表 @handler GetRecommandProductListHandler get /api/product/recommand (GetRecommandProductListReq) returns (response); + //获取列表页推荐产品列表 + @handler HomePageRecommandProductListHandler + get /api/product/home_page_recommand (HomePageRecommandProductListReq) returns (response); //*********************产品详情分解接口结束*********************** //*********************推荐产品接口开始×××××××××××××××××××××××××× @@ -417,4 +420,22 @@ type GetLastProductDesignRsp { SizeId int64 `json:"size_id"` LogoColor interface{} `json:"logo_color"` Info interface{} `json:"info"` +} +//获取列表页推荐产品(返回是这个维度数组) +type HomePageRecommandProductListReq { + Size uint32 `form:"size"` +} +type HomePageRecommandProductListRsp { + ProductId int64 `json:"product_id"` + Sn string `json:"sn"` + Title string `json:"title"` + Cover string `json:"cover"` + Intro string `json:"intro"` + CoverImg string `json:"cover_img"` + IsEnv int64 `json:"is_env"` + IsMicro int64 `json:"is_micro"` + SizeNum uint32 `json:"size_num"` + MiniPrice int64 `json:"mini_price"` + CoverDefault string `json:"cover_default"` + HaveOptionalFitting bool `json:"have_optional_fitting"` } \ No newline at end of file