diff --git a/constants/cart.go b/constants/cart.go new file mode 100644 index 00000000..45691f26 --- /dev/null +++ b/constants/cart.go @@ -0,0 +1,3 @@ +package constants + +const CART_EXPIRE_TIME = 60 //购物车过期时间 diff --git a/model/gmodel/fscartmodel.go b/model/gmodel/fscartmodel.go index 845ef339..becef07c 100755 --- a/model/gmodel/fscartmodel.go +++ b/model/gmodel/fscartmodel.go @@ -96,3 +96,17 @@ func (c *FsCartModel) CountUserCart(ctx context.Context, userId int64) (total in } return } +func (c *FsCartModel) GetAllByUserId(ctx context.Context, userId int64, sort string) (resp []FsCart, err error) { + db := c.db.WithContext(ctx).Model(&FsCart{}).Where("`user_id` = ? and `status` = ?", userId, 1) + switch sort { + case "ctime-desc": + db = db.Order("`ctime` DESC") + case "ctime-asc": + db = db.Order("`ctime` ASC") + } + err = db.Find(&resp).Error + if err != nil { + return nil, err + } + return +} diff --git a/model/gmodel/fsproductdesignmodel.go b/model/gmodel/fsproductdesignmodel.go index e79c92d9..2ee88d4d 100755 --- a/model/gmodel/fsproductdesignmodel.go +++ b/model/gmodel/fsproductdesignmodel.go @@ -40,3 +40,13 @@ func (d *FsProductDesignModel) FindOneBySn(ctx context.Context, sn string) (resp } return resp, nil } +func (d *FsProductDesignModel) GetAllByIds(ctx context.Context, ids []int64) (resp []FsProductDesign, err error) { + if len(ids) == 0 { + return + } + err = d.db.WithContext(ctx).Model(&FsProductDesign{}).Where("`id` in (?) and `status` = ?", ids, 1).Find(&resp).Error + if err != nil { + return nil, err + } + return +} diff --git a/model/gmodel/fsproductmodel3dmodel.go b/model/gmodel/fsproductmodel3dmodel.go index 1f215b6b..b2a3b3f9 100755 --- a/model/gmodel/fsproductmodel3dmodel.go +++ b/model/gmodel/fsproductmodel3dmodel.go @@ -1,6 +1,9 @@ package gmodel -import "gorm.io/gorm" +import ( + "context" + "gorm.io/gorm" +) type FsProductModel3d struct { Id int64 `gorm:"primary_key" json:"id"` @@ -29,3 +32,23 @@ type FsProductModel3dModel struct { func NewFsProductModel3dModel(db *gorm.DB) *FsProductModel3dModel { return &FsProductModel3dModel{db} } +func (d *FsProductModel3dModel) GetAllByIds(ctx context.Context, ids []int64) (resp []FsProductModel3d, err error) { + if len(ids) == 0 { + return + } + err = d.db.WithContext(ctx).Model(&FsProductModel3d{}).Where("`id` in (?) and `status` = ?", ids, 1).Find(&resp).Error + if err != nil { + return nil, err + } + return +} +func (d *FsProductModel3dModel) GetAllByIdsTag(ctx context.Context, ids []int64, tag int64) (resp []FsProductModel3d, err error) { + if len(ids) == 0 { + return + } + err = d.db.WithContext(ctx).Model(&FsProductModel3d{}).Where("`id` in (?) and `status` = ? and `tag` = ?", ids, 1, tag).Find(&resp).Error + if err != nil { + return nil, err + } + return +} diff --git a/model/gmodel/fsproductpricemodel.go b/model/gmodel/fsproductpricemodel.go index 7c58ac1a..0e988b6c 100755 --- a/model/gmodel/fsproductpricemodel.go +++ b/model/gmodel/fsproductpricemodel.go @@ -34,7 +34,7 @@ type GetPriceListByProductIdsRsp struct { Price string `json:"price"` } -func (p *FsProductPriceModel) GetPriceListByProductIds(ctx context.Context, productIds []int64) (resp []GetPriceListByProductIdsRsp, err error) { +func (p *FsProductPriceModel) GetSimplePriceListByProductIds(ctx context.Context, productIds []int64) (resp []GetPriceListByProductIdsRsp, err error) { if len(productIds) == 0 { return nil, nil } @@ -82,3 +82,14 @@ func (p *FsProductPriceModel) FindOneProductPriceByParams(ctx context.Context, r } return resp, nil } +func (p *FsProductPriceModel) GetPriceListByProductIds(ctx context.Context, productIds []int64) (resp []FsProductPrice, err error) { + if len(productIds) == 0 { + return nil, nil + } + db := p.db.WithContext(ctx).Model(&FsProductPrice{}). + Where("`product_id` in (?) and `status` = ?", productIds, 1) + if err = db.Find(&resp).Error; err != nil { + return nil, err + } + return +} diff --git a/server/product/internal/logic/getproductlistlogic.go b/server/product/internal/logic/getproductlistlogic.go index 277f8522..86e7021d 100644 --- a/server/product/internal/logic/getproductlistlogic.go +++ b/server/product/internal/logic/getproductlistlogic.go @@ -77,7 +77,7 @@ func (l *GetProductListLogic) GetProductList(req *types.GetProductListReq, login productIds = append(productIds, v.Id) } productPriceModel := gmodel.NewFsProductPriceModel(l.svcCtx.MysqlConn) - productPriceList, err := productPriceModel.GetPriceListByProductIds(l.ctx, productIds) + productPriceList, err := productPriceModel.GetSimplePriceListByProductIds(l.ctx, productIds) if err != nil { logx.Error(err) return resp.SetStatusWithMessage(basic.CodeServiceErr, "failed to get product min price list") diff --git a/server/product/internal/logic/getsizebyproductlogic.go b/server/product/internal/logic/getsizebyproductlogic.go index e01c4a10..213f5792 100644 --- a/server/product/internal/logic/getsizebyproductlogic.go +++ b/server/product/internal/logic/getsizebyproductlogic.go @@ -9,6 +9,7 @@ import ( "fusenapi/utils/auth" "fusenapi/utils/basic" "fusenapi/utils/format" + "fusenapi/utils/step_price" "strings" "fusenapi/server/product/internal/svc" @@ -167,7 +168,7 @@ func (l *GetSizeByProductLogic) GetSecondChildrenList(product gmodel.FsProduct, for int(*price.MinBuyNum) < (stepNum[len(stepNum)-1]+5) && index < 3 { priceList = append(priceList, types.PriceObj{ Num: int(*price.MinBuyNum * *price.EachBoxNum), - Price: l.GetPrice(int(*price.MinBuyNum), stepNum, stepPrice), + Price: step_price.GetStepPrice(int(*price.MinBuyNum), stepNum, stepPrice), }) *price.MinBuyNum++ index++ @@ -181,17 +182,3 @@ func (l *GetSizeByProductLogic) GetSecondChildrenList(product gmodel.FsProduct, } return } -func (l *GetSizeByProductLogic) GetPrice(minBuyNum int, stepNum []int, stepPrice []int) float64 { - if minBuyNum > stepNum[len(stepNum)-1] { - return float64(stepPrice[len(stepPrice)-1]) / float64(100) - } - for k, v := range stepNum { - if minBuyNum <= v { - if k <= (len(stepPrice) - 1) { - return float64(stepPrice[k]) / float64(100) - } - return float64(stepPrice[len(stepPrice)-1]) / float64(100) - } - } - return float64(stepPrice[len(stepPrice)-1]) / float64(100) -} diff --git a/server/shopping-cart-confirmation/internal/handler/cartlisthandler.go b/server/shopping-cart-confirmation/internal/handler/cartlisthandler.go new file mode 100644 index 00000000..8e45bb62 --- /dev/null +++ b/server/shopping-cart-confirmation/internal/handler/cartlisthandler.go @@ -0,0 +1,67 @@ +package handler + +import ( + "errors" + "net/http" + + "github.com/zeromicro/go-zero/core/logx" + "github.com/zeromicro/go-zero/rest/httpx" + + "fusenapi/utils/auth" + "fusenapi/utils/basic" + + "fusenapi/server/shopping-cart-confirmation/internal/logic" + "fusenapi/server/shopping-cart-confirmation/internal/svc" + "fusenapi/server/shopping-cart-confirmation/internal/types" +) + +func CartListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + // 解析jwtToken + /*claims, err := svcCtx.ParseJwtToken(r) + // 如果解析出错,则返回未授权的JSON响应并记录错误消息 + if err != nil { + httpx.OkJsonCtx(r.Context(), w, &basic.Response{ + Code: 401, + Message: "unauthorized", + }) + logx.Info("unauthorized:", err.Error()) + return + } + + // 从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 + } + */ + var req types.CartListReq + // 如果端点有请求结构体,则使用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.NewCartListLogic(r.Context(), svcCtx) + resp := l.CartList(&req, &auth.UserInfo{86}) + // 如果响应不为nil,则使用httpx.OkJsonCtx方法返回JSON响应; + // 否则,发送500内部服务器错误的JSON响应并记录错误消息logx.Error。 + 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/shopping-cart-confirmation/internal/handler/routes.go b/server/shopping-cart-confirmation/internal/handler/routes.go index 1b4fc579..a3a88832 100644 --- a/server/shopping-cart-confirmation/internal/handler/routes.go +++ b/server/shopping-cart-confirmation/internal/handler/routes.go @@ -27,6 +27,11 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { Path: "/cart/num", Handler: CartNumberHandler(serverCtx), }, + { + Method: http.MethodGet, + Path: "/cart/list", + Handler: CartListHandler(serverCtx), + }, }, ) } diff --git a/server/shopping-cart-confirmation/internal/logic/cartlistlogic.go b/server/shopping-cart-confirmation/internal/logic/cartlistlogic.go new file mode 100644 index 00000000..fa138655 --- /dev/null +++ b/server/shopping-cart-confirmation/internal/logic/cartlistlogic.go @@ -0,0 +1,273 @@ +package logic + +import ( + "encoding/json" + "errors" + "fusenapi/constants" + "fusenapi/model/gmodel" + "fusenapi/utils/auth" + "fusenapi/utils/basic" + "fusenapi/utils/format" + "fusenapi/utils/image" + "fusenapi/utils/step_price" + "strings" + "sync" + "time" + + "context" + + "fusenapi/server/shopping-cart-confirmation/internal/svc" + "fusenapi/server/shopping-cart-confirmation/internal/types" + + "github.com/zeromicro/go-zero/core/logx" +) + +type CartListLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func NewCartListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CartListLogic { + return &CartListLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +// 获取用户购物车列表 +func (l *CartListLogic) CartList(req *types.CartListReq, userinfo *auth.UserInfo) (resp *basic.Response) { + //获取当前图片应该返回的尺寸大小 + if req.Size > 0 { + req.Size = image.GetCurrentSize(req.Size) + } + //获取用户购物车数据 + cartRelativeData, err := l.getUserCartRelativeList(l.ctx, userinfo.UserId) + if err != nil { + logx.Error(err) + return resp.SetStatusWithMessage(basic.CodeServiceErr, "failed to get cart list data") + } + if len(cartRelativeData.CartList) == 0 { + return resp.SetStatusWithMessage(basic.CodeOK, "success") + } + //产品名map + mapProduct := make(map[int64]int) + for k, v := range cartRelativeData.ProductList { + mapProduct[v.Id] = k + } + //产品尺寸size map + mapProductSize := make(map[int64]int) + for k, v := range cartRelativeData.ProductSizeList { + mapProductSize[v.Id] = k + } + //设计map数据 + mapProductDesign := make(map[int64]int) + for k, v := range cartRelativeData.ProductDesignList { + mapProductDesign[v.Id] = k + } + //产品模型map数据 + mapProductModel3d := make(map[int64]int) + for k, v := range cartRelativeData.ProductModel3dList { + mapProductModel3d[v.Id] = k + } + //遍历组装数据 + rspList := make([]types.CartListRsp, 0, len(cartRelativeData.CartList)) + for _, v := range cartRelativeData.CartList { + name := "" + productSn := "" + if productIndex, ok := mapProduct[*v.ProductId]; ok { + name = *cartRelativeData.ProductList[productIndex].Title + productSn = strings.ToLower(*cartRelativeData.ProductList[productIndex].Sn) + } + capacity := "" + var sizeList types.CartSizeItem + if sizeIndex, ok := mapProductSize[*v.SizeId]; ok { + capacity = *cartRelativeData.ProductSizeList[sizeIndex].Capacity + err = json.Unmarshal([]byte(*cartRelativeData.ProductSizeList[sizeIndex].Title), &sizeList) + if err != nil { + logx.Error(err) + return resp.SetStatusWithMessage(basic.CodeServiceErr, "failed to parse product size`s title") + } + } + designSn := "" + if designIndex, ok := mapProductDesign[*v.DesignId]; ok { + designSn = strings.ToLower(*cartRelativeData.ProductDesignList[designIndex].Sn) + } + option := types.CartOption{} + if model3dIndex, ok := mapProductModel3d[*v.OptionalId]; ok { + option.Id = cartRelativeData.ProductModel3dList[model3dIndex].Id + option.Title = *cartRelativeData.ProductModel3dList[model3dIndex].Title + option.Price = float64(*cartRelativeData.ProductModel3dList[model3dIndex].Price) / float64(100) + } + pcList, err := l.getPcList(cartRelativeData.ProductPriceList, *v.ProductId, *v.MaterialId, *v.SizeId) + if err != nil { + logx.Error(err) + return resp.SetStatusWithMessage(basic.CodeServiceErr, "failed to calculate step price") + } + rspList = append(rspList, types.CartListRsp{ + Id: v.Id, + Cover: *v.Cover, + Name: name, + Capacity: capacity, + ETA: time.Now().AddDate(0, 0, 60).Format("2006-01-02 15:04:05"), + Pcs: *v.BuyNum, + ProductSn: productSn, + DesignSn: designSn, + Option: option, + IsCheck: *v.IsCheck, + TsTime: v.TsTime.Format("2006-01-02 15:04:05"), + PcsList: pcList, + Size: sizeList, + }) + } + return resp.SetStatusWithMessage(basic.CodeOK, "success", rspList) +} + +type GetUserCartRelativeListRsp struct { + CartList []gmodel.FsCart + ProductDesignList []gmodel.FsProductDesign + ProductSizeList []gmodel.FsProductSize + ProductList []gmodel.FsProduct + ProductModel3dList []gmodel.FsProductModel3d + ProductTemplateV2List []gmodel.FsProductTemplateV2 + ProductPriceList []gmodel.FsProductPrice +} + +// 获取阶梯对应的价格 +func (l *CartListLogic) getPcList(productPriceList []gmodel.FsProductPrice, productId int64, materialId int64, sizeId int64) (list []types.PcsItem, err error) { + for _, price := range productPriceList { + if *price.ProductId != productId || *price.MaterialId != materialId || *price.SizeId != sizeId { + continue + } + stepNumStrSlice := strings.Split(*price.StepNum, ",") + stepNumSlice, err := format.StrSlicToIntSlice(stepNumStrSlice) + if err != nil { + return nil, err + } + stepPriceStrSlice := strings.Split(*price.StepPrice, ",") + stepPriceSlice, err := format.StrSlicToIntSlice(stepPriceStrSlice) + if err != nil { + return nil, err + } + lenStepPriceSlice := len(stepPriceSlice) + lenStepNumSlice := len(stepNumSlice) + if lenStepPriceSlice == 0 || lenStepNumSlice == 0 { + return nil, errors.New("step num or step price item count can`t be 0 ") + } + for int(*price.MinBuyNum) < stepNumSlice[lenStepNumSlice-1]+5 { + list = append(list, types.PcsItem{ + Num: *price.MinBuyNum, + TotalNum: *price.MinBuyNum * *price.EachBoxNum, + Title: *price.Title, + Price: step_price.GetStepPrice(int(*price.MinBuyNum), stepNumSlice, stepPriceSlice), + }) + *price.MinBuyNum++ + } + } + return list, nil +} + +// 获取用户购物车数据 +func (l *CartListLogic) getUserCartRelativeList(ctx context.Context, userId int64) (resp GetUserCartRelativeListRsp, err error) { + //购物车列表 + cartModel := gmodel.NewFsCartModel(l.svcCtx.MysqlConn) + cartList, err := cartModel.GetAllByUserId(ctx, userId, "ctime-desc") + if err != nil { + return GetUserCartRelativeListRsp{}, err + } + if len(cartList) == 0 { + return + } + resp.CartList = cartList + productIds := make([]int64, 0, len(cartList)) + sizeIds := make([]int64, 0, len(cartList)) + designIds := make([]int64, 0, len(cartList)) + optionIds := make([]int64, 0, len(cartList)) + templateIds := make([]int64, 0, len(cartList)) + for _, v := range cartList { + productIds = append(productIds, *v.ProductId) + sizeIds = append(sizeIds, *v.SizeId) + designIds = append(designIds, *v.DesignId) + optionIds = append(optionIds, *v.OptionalId) + templateIds = append(templateIds, *v.TemplateId) + } + //******************************协程并发开始*********************************** + wait := sync.WaitGroup{} + gorutine := 6 //注意要加协程这里要记得加 + errChan := make(chan error, gorutine) + wait.Add(gorutine) + //获取产品数据1 + go func() { + defer wait.Done() + productModel := gmodel.NewFsProductModel(l.svcCtx.MysqlConn) + productList, err := productModel.GetProductListByIds(ctx, productIds, "") + if err != nil { + errChan <- err + return + } + resp.ProductList = productList + }() + //获取尺寸数据2 + go func() { + defer wait.Done() + productSizeModel := gmodel.NewFsProductSizeModel(l.svcCtx.MysqlConn) + productSizeList, err := productSizeModel.GetAllByIds(ctx, sizeIds, "") + if err != nil { + errChan <- err + } + resp.ProductSizeList = productSizeList + }() + //获取设计列表3 + go func() { + defer wait.Done() + productDesignModel := gmodel.NewFsProductDesignModel(l.svcCtx.MysqlConn) + productDesignList, err := productDesignModel.GetAllByIds(ctx, designIds) + if err != nil { + errChan <- err + } + resp.ProductDesignList = productDesignList + }() + //获取配件列表4 + go func() { + defer wait.Done() + productModel3dModel := gmodel.NewFsProductModel3dModel(l.svcCtx.MysqlConn) + productModel3dList, err := productModel3dModel.GetAllByIdsTag(ctx, optionIds, constants.TAG_PARTS) + if err != nil { + errChan <- err + } + resp.ProductModel3dList = productModel3dList + }() + //获取模板信息5 + go func() { + defer wait.Done() + productTemplateModel := gmodel.NewFsProductTemplateV2Model(l.svcCtx.MysqlConn) + templateV2List, err := productTemplateModel.FindAllByProductIds(ctx, templateIds) + if err != nil { + errChan <- err + } + resp.ProductTemplateV2List = templateV2List + }() + //获取产品价格列表6 + go func() { + defer wait.Done() + productPriceModel := gmodel.NewFsProductPriceModel(l.svcCtx.MysqlConn) + productPriceList, err := productPriceModel.GetPriceListByProductIds(ctx, productIds) + if err != nil { + errChan <- err + } + resp.ProductPriceList = productPriceList + }() + //******************************协程并发结束*********************************** + go func() { + wait.Wait() + close(errChan) + }() + //获取错误信息 + for err = range errChan { + if err != nil { + return GetUserCartRelativeListRsp{}, err + } + } + return resp, nil +} diff --git a/server/shopping-cart-confirmation/internal/types/types.go b/server/shopping-cart-confirmation/internal/types/types.go index c1bedda4..98a5a2c9 100644 --- a/server/shopping-cart-confirmation/internal/types/types.go +++ b/server/shopping-cart-confirmation/internal/types/types.go @@ -19,6 +19,44 @@ type CartNumberRsp struct { Num int64 `json:"num"` } +type CartListReq struct { + Size uint32 `form:"size"` +} + +type CartListRsp struct { + Id int64 `json:"id"` + Cover string `json:"cover"` + Name string `json:"name"` + Capacity string `json:"capacity"` + ETA string `json:"ETA"` + Pcs int64 `json:"pcs"` + ProductSn string `json:"product_sn"` + DesignSn string `json:"designSn"` + Option CartOption `json:"option"` + IsCheck int64 `json:"is_check"` + TsTime string `json:"ts_time"` + PcsList []PcsItem `json:"pcs_list"` + Size CartSizeItem `json:"size"` +} + +type CartOption struct { + Id int64 `json:"id"` + Title string `json:"title"` + Price float64 `json:"price"` +} + +type PcsItem struct { + Num int64 `json:"num"` + TotalNum int64 `json:"total_num"` + Title string `json:"title"` + Price float64 `json:"price"` +} + +type CartSizeItem struct { + Cm string `json:"cm"` + Inch string `json:"inch"` +} + type Response struct { Code int `json:"code"` Message string `json:"msg"` diff --git a/server_api/shopping-cart-confirmation.api b/server_api/shopping-cart-confirmation.api index a67187b2..1de7d33f 100644 --- a/server_api/shopping-cart-confirmation.api +++ b/server_api/shopping-cart-confirmation.api @@ -17,6 +17,9 @@ service shopping-cart-confirmation { //获取用户购物车数量 @handler CartNumberHandler get /cart/num ( ) returns (response); + //获取用户购物车列表 + @handler CartListHandler + get /cart/list (CartListReq) returns (response); } //添加入购物车 @@ -32,4 +35,38 @@ type CartDeleteReq { //获取用户购物车数量 type CartNumberRsp { Num int64 `json:"num"` +} +//获取用户购物车列表 +type CartListReq { + Size uint32 `form:"size"` +} +type CartListRsp { + Id int64 `json:"id"` + Cover string `json:"cover"` + Name string `json:"name"` + Capacity string `json:"capacity"` + ETA string `json:"ETA"` + Pcs int64 `json:"pcs"` + ProductSn string `json:"product_sn"` + DesignSn string `json:"designSn"` + Option CartOption `json:"option"` + IsCheck int64 `json:"is_check"` + TsTime string `json:"ts_time"` + PcsList []PcsItem `json:"pcs_list"` + Size CartSizeItem `json:"size"` +} +type CartOption { + Id int64 `json:"id"` + Title string `json:"title"` + Price float64 `json:"price"` +} +type PcsItem { + Num int64 `json:"num"` + TotalNum int64 `json:"total_num"` + Title string `json:"title"` + Price float64 `json:"price"` +} +type CartSizeItem { + Cm string `json:"cm"` + Inch string `json:"inch"` } \ No newline at end of file diff --git a/utils/step_price/price.go b/utils/step_price/price.go new file mode 100644 index 00000000..6c50ee6c --- /dev/null +++ b/utils/step_price/price.go @@ -0,0 +1,16 @@ +package step_price + +func GetStepPrice(minBuyNum int, stepNum []int, stepPrice []int) float64 { + if minBuyNum > stepNum[len(stepNum)-1] { + return float64(stepPrice[len(stepPrice)-1]) / float64(100) + } + for k, v := range stepNum { + if minBuyNum <= v { + if k <= (len(stepPrice) - 1) { + return float64(stepPrice[k]) / float64(100) + } + return float64(stepPrice[len(stepPrice)-1]) / float64(100) + } + } + return float64(stepPrice[len(stepPrice)-1]) / float64(100) +}