diff --git a/constants/websocket.go b/constants/websocket.go index 84321556..e3cf11f9 100644 --- a/constants/websocket.go +++ b/constants/websocket.go @@ -2,28 +2,29 @@ package constants type Websocket string -// websocket消息类型 +// websocket消息类型(主类别) const ( - //鉴权失败 - WEBSOCKET_UNAUTH Websocket = "WEBSOCKET_UNAUTH" - //获取ws连接标识错误 - WEBSOCKET_GEN_UNIQUE_ID_ERR Websocket = "WEBSOCKET_GEN_UNIQUE_ID_ERR" - //ws连接成功 - WEBSOCKET_CONNECT_SUCCESS Websocket = "WEBSOCKET_CONNECT_SUCCESS" - //请求恢复为上次连接的标识 - WEBSOCKET_REQUEST_REUSE_LAST_CONNECT Websocket = "WEBSOCKET_REQUEST_REUSE_LAST_CONNECT" - //请求恢复为上次连接的标识错误 - WEBSOCKET_REQUEST_RESUME_LAST_CONNECT_ERR Websocket = "WEBSOCKET_REQUEST_RESUME_LAST_CONNECT_ERR" - //图片渲染消息 - WEBSOCKET_RENDER_IMAGE Websocket = "WEBSOCKET_RENDER_IMAGE" - //图片渲染失败消息 - WEBSOCKET_RENDER_IMAGE_ERR Websocket = "WEBSOCKET_RENDER_IMAGE_ERR" - //反回合成刀版图消息 - WEBSOCKET_COMBINE_IMAGE Websocket = "WEBSOCKET_COMBINE_IMAGE" - //传入数据格式错误 - WEBSOCKET_ERR_DATA_FORMAT Websocket = "WEBSOCKET_ERR_DATA_FORMAT" - //通用回调通知 - WEBSOCKET_COMMON_NOTIFY Websocket = "WEBSOCKET_COMMON_NOTIFY" - //数据接收速度超过数据消费速度(缓冲队列满了) - WEBSOCKET_INCOME_CACHE_QUEUE_OVERFLOW = "WEBSOCKET_INCOME_CACHE_QUEUE_OVERFLOW" + WEBSOCKET_UNAUTH Websocket = "WEBSOCKET_UNAUTH" //鉴权失败 (1级消息,单向通信) + WEBSOCKET_GEN_UNIQUE_ID_ERR Websocket = "WEBSOCKET_GEN_UNIQUE_ID_ERR" //获取ws连接标识错误 (1级消息,单向通信) + WEBSOCKET_CONNECT_SUCCESS Websocket = "WEBSOCKET_CONNECT_SUCCESS" //ws连接成功 (1级消息,单向通信) + WEBSOCKET_REQUEST_REUSE_LAST_CONNECT Websocket = "WEBSOCKET_REQUEST_REUSE_LAST_CONNECT" //请求恢复为上次连接的标识 (1级消息,单向通信) + WEBSOCKET_REQUEST_RESUME_LAST_CONNECT_ERR Websocket = "WEBSOCKET_REQUEST_RESUME_LAST_CONNECT_ERR" //请求恢复为上次连接的标识错误 (1级消息,单向通信) + WEBSOCKET_INCOME_CACHE_QUEUE_OVERFLOW = "WEBSOCKET_INCOME_CACHE_QUEUE_OVERFLOW" //数据接收速度超过数据消费速度(缓冲队列满了)(1级消息,单向通信) +) + +// websocket消息类型(通用通知类别) +const ( + WEBSOCKET_COMMON_NOTIFY Websocket = "WEBSOCKET_COMMON_NOTIFY" //通用回调通知(1级消息,单向通信) +) + +// websocket消息类型(基本传输结构类别) +const ( + WEBSOCKET_ERR_DATA_FORMAT Websocket = "WEBSOCKET_ERR_DATA_FORMAT" //传入数据格式错误(1级消息,单向通信) +) + +// websocket消息类型(云渲染类别) +const ( + WEBSOCKET_RENDER_IMAGE Websocket = "WEBSOCKET_RENDER_IMAGE" //图片渲染消息(1级消息,双向通信) + WEBSOCKET_RENDER_IMAGE_ERR Websocket = "WEBSOCKET_RENDER_IMAGE_ERR" //图片渲染失败消息(1级消息,单向通信) + WEBSOCKET_COMBINE_IMAGE Websocket = "WEBSOCKET_COMBINE_IMAGE" //反回合成刀版图消息(2级消息,单向通信,属于 WEBSOCKET_RENDER_IMAGE 消息的子流程) ) diff --git a/server/product/internal/logic/gettagproductlistlogic.go b/server/product/internal/logic/gettagproductlistlogic.go index b69736d0..b1132393 100644 --- a/server/product/internal/logic/gettagproductlistlogic.go +++ b/server/product/internal/logic/gettagproductlistlogic.go @@ -85,117 +85,26 @@ func (l *GetTagProductListLogic) GetTagProductList(req *types.GetTagProductListR var ( productList []gmodel.FsProduct //产品列表(select 字段需要看查询的地方) mapTagProp = make(map[int64][]types.CoverDefaultItem) - 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]int64) //产品模板map + mapProductMinPrice = make(map[int64]int64) //产品最小价格map + mapProductSizeCount = make(map[int64]int64) //产品尺寸数量map + mapProductTemplate = make(map[int64]int64) //产品模板map ) //携带产品 if req.WithProduct { - //查询符合的产品列表 - pIsDel := int64(0) - pStatus := int64(1) - pIsShelf := int64(1) - //获取产品列表 - productList, err = l.svcCtx.AllModels.FsProduct.GetProductListByParams(l.ctx, - gmodel.GetProductListByParamsReq{ - Type: typeIds, - IsDel: &pIsDel, - IsShelf: &pIsShelf, - Status: &pStatus, - OrderBy: "`is_recommend` DESC,`sort` ASC", - }) + productList, err = l.getProductRelationInfo(getProductRelationInfoReq{ + Ctx: l.ctx, + TemplateTag: req.TemplateTag, + TypeIds: typeIds, + MapTagProp: mapTagProp, + MapProductHaveOptionFitting: mapProductHaveOptionFitting, + MapProductMinPrice: mapProductMinPrice, + MapProductSizeCount: mapProductSizeCount, + MapProductTemplate: mapProductTemplate, + }) if err != nil { logx.Error(err) - return resp.SetStatusWithMessage(basic.CodeServiceErr, "failed to get product list") - } - productIds := make([]int64, 0, len(productList)) - for _, product := range productList { - 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{}{} - } - //获取产品标签相关属性 - productTagPropList, err := l.svcCtx.AllModels.FsProductTagProp.GetTagPropByProductIdsWithProductTag(l.ctx, productIds) - if err != nil { - logx.Error(err) - return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "failed to get product tag property") - } - for _, v := range productTagPropList { - mapTagProp[*v.ProductId] = append(mapTagProp[*v.ProductId], types.CoverDefaultItem{ - Tag: v.TemplateTag, - Cover: *v.Cover, - }) - } - //获取产品价格列表 - 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,id) - if req.TemplateTag != "" { //指定模板tag - productTemplatesV2, err = l.svcCtx.AllModels.FsProductTemplateV2.FindAllByProductIdsTemplateTag(l.ctx, productIds, req.TemplateTag, "sort ASC", "product_id,id") - } else { //没指定模板tag - productTemplatesV2, err = l.svcCtx.AllModels.FsProductTemplateV2.FindAllByProductIds(l.ctx, productIds, "sort ASC", "product_id,id") - } - if err != nil { - logx.Error(err) - return resp.SetStatusWithMessage(basic.CodeServiceErr, "failed to get product templates") - } - //只存第一个 - for _, v := range productTemplatesV2 { - if _, ok := mapProductTemplate[*v.ProductId]; ok { - continue - } - mapProductTemplate[*v.ProductId] = v.Id - } - //获取产品尺寸数量 - 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 + return resp.SetStatusWithMessage(basic.CodeServiceErr, "failed to get product series info") } } //map tag菜单 @@ -227,6 +136,128 @@ func (l *GetTagProductListLogic) GetTagProductList(req *types.GetTagProductListR }) } +// 如果携带产品,就查询产品相关信息 +type getProductRelationInfoReq struct { + Ctx context.Context + TemplateTag string + TypeIds []int64 + MapTagProp map[int64][]types.CoverDefaultItem + MapProductHaveOptionFitting map[int64]struct{} + MapProductMinPrice map[int64]int64 + MapProductSizeCount map[int64]int64 + MapProductTemplate map[int64]int64 +} + +func (l *GetTagProductListLogic) getProductRelationInfo(req getProductRelationInfoReq) (productList []gmodel.FsProduct, err error) { + var ( + productTemplatesV2List []gmodel.FsProductTemplateV2 + productSizeCountList []gmodel.CountProductSizeByStatusRsp + productOptionalPartList []gmodel.GetGroupPartListByProductIdsRsp + ) + //查询符合的产品列表 + pIsDel := int64(0) + pStatus := int64(1) + pIsShelf := int64(1) + //获取产品列表 + productList, err = l.svcCtx.AllModels.FsProduct.GetProductListByParams(l.ctx, + gmodel.GetProductListByParamsReq{ + Type: req.TypeIds, + IsDel: &pIsDel, + IsShelf: &pIsShelf, + Status: &pStatus, + OrderBy: "`is_recommend` DESC,`sort` ASC", + }) + if err != nil { + logx.Error(err) + return nil, errors.New("failed to get product list") + } + productIds := make([]int64, 0, len(productList)) + for _, product := range productList { + productIds = append(productIds, product.Id) + } + //获取商品可选配件 + productOptionalPartList, err = l.svcCtx.AllModels.FsProductModel3d.GetGroupPartListByProductIds(l.ctx, productIds) + if err != nil { + logx.Error(err) + return nil, errors.New("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 + } + req.MapProductHaveOptionFitting[partList.ProductId] = struct{}{} + } + //获取产品标签相关属性 + productTagPropList, err := l.svcCtx.AllModels.FsProductTagProp.GetTagPropByProductIdsWithProductTag(l.ctx, productIds) + if err != nil { + logx.Error(err) + return nil, errors.New("failed to get product tag property") + } + for _, v := range productTagPropList { + req.MapTagProp[*v.ProductId] = append(req.MapTagProp[*v.ProductId], types.CoverDefaultItem{ + Tag: v.TemplateTag, + Cover: *v.Cover, + }) + } + //获取产品价格列表 + productPriceList, err := l.svcCtx.AllModels.FsProductPrice.GetSimplePriceListByProductIds(l.ctx, productIds) + if err != nil { + logx.Error(err) + return nil, errors.New("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 nil, errors.New("parse price err") + } + if len(priceSlice) == 0 { + continue + } + //正序排序价格(注意排序后的阶梯价格不能用作阶梯数量价格计算) + sort.Ints(priceSlice) + if min, ok := req.MapProductMinPrice[v.ProductId]; ok { + if min > int64(priceSlice[0]) { + req.MapProductMinPrice[v.ProductId] = int64(priceSlice[0]) + } + } else { + req.MapProductMinPrice[v.ProductId] = int64(priceSlice[0]) + } + } + //获取模板(只是获取产品product_id,id) + if req.TemplateTag != "" { //指定模板tag + productTemplatesV2List, err = l.svcCtx.AllModels.FsProductTemplateV2.FindAllByProductIdsTemplateTag(l.ctx, productIds, req.TemplateTag, "sort ASC", "product_id,id") + } else { //没指定模板tag + productTemplatesV2List, err = l.svcCtx.AllModels.FsProductTemplateV2.FindAllByProductIds(l.ctx, productIds, "sort ASC", "product_id,id") + } + if err != nil { + logx.Error(err) + return nil, errors.New("failed to get product templates") + } + //只存第一个 + for _, v := range productTemplatesV2List { + if _, ok := req.MapProductTemplate[*v.ProductId]; ok { + continue + } + req.MapProductTemplate[*v.ProductId] = v.Id + } + //获取产品尺寸数量 + productSizeCountList, err = l.svcCtx.AllModels.FsProductSize.GetGroupProductSizeByStatus(l.ctx, productIds, 1) + if err != nil { + logx.Error(err) + return nil, errors.New("get product size count err") + } + for _, v := range productSizeCountList { + req.MapProductSizeCount[v.ProductId] = v.Num + } + return productList, nil +} + // 处理tag菜单数据 type dealWithTagMenuDataReq struct { TagList []gmodel.FsTags @@ -351,6 +382,7 @@ type getTagProductsReq struct { User gmodel.FsUser } +// 返回每个标签下的直接从属产品,不包含子tag的产品 func (l *GetTagProductListLogic) getTagProducts(req getTagProductsReq) (productListRsp []types.TagProduct) { //默认给50个容量 productListRsp = make([]types.TagProduct, 0, 50) diff --git a/server/websocket/internal/logic/allocation_processing_factory.go b/server/websocket/internal/logic/allocation_processing_factory.go new file mode 100644 index 00000000..18cce02b --- /dev/null +++ b/server/websocket/internal/logic/allocation_processing_factory.go @@ -0,0 +1,33 @@ +package logic + +import ( + "fusenapi/constants" +) + +// 消息分发工厂 +type allocationProcessorFactory interface { + allocationMessage(w *wsConnectItem, data []byte) +} + +var mapAllocationProcessor = make(map[constants.Websocket]allocationProcessorFactory) + +func (w *wsConnectItem) newAllocationProcessor(msgType constants.Websocket) allocationProcessorFactory { + if val, ok := mapAllocationProcessor[msgType]; ok { + return val + } + var obj allocationProcessorFactory + switch msgType { + //图片渲染 + case constants.WEBSOCKET_RENDER_IMAGE: + obj = &renderProcessor{} + //刷新重连请求恢复上次连接的标识 + case constants.WEBSOCKET_REQUEST_REUSE_LAST_CONNECT: + obj = &reuseConnProcessor{} + default: + + } + if obj != nil { + mapAllocationProcessor[msgType] = obj + } + return obj +} diff --git a/server/websocket/internal/logic/datatransferlogic.go b/server/websocket/internal/logic/datatransferlogic.go index 9bc386b7..eb6434b8 100644 --- a/server/websocket/internal/logic/datatransferlogic.go +++ b/server/websocket/internal/logic/datatransferlogic.go @@ -313,7 +313,8 @@ func (w *wsConnectItem) consumeInChanData() { case <-w.closeChan: return case data := <-w.inChan: - w.dealwithReciveData(data) + //对不同消息类型分发处理 + w.allocationProcessing(data) } } } @@ -382,7 +383,7 @@ func (w *wsConnectItem) respondDataFormat(msgType constants.Websocket, data inte } // 处理入口缓冲队列中不同类型的数据(分发处理) -func (w *wsConnectItem) dealwithReciveData(data []byte) { +func (w *wsConnectItem) allocationProcessing(data []byte) { var parseInfo websocket_data.DataTransferData if err := json.Unmarshal(data, &parseInfo); err != nil { w.incomeDataFormatErrResponse("invalid format of income message:" + string(data)) @@ -390,15 +391,12 @@ func (w *wsConnectItem) dealwithReciveData(data []byte) { return } d, _ := json.Marshal(parseInfo.D) - //分消息类型给到不同逻辑处理,可扩展 - switch parseInfo.T { - //图片渲染 - case constants.WEBSOCKET_RENDER_IMAGE: - w.sendToRenderChan(d) - //刷新重连请求恢复上次连接的标识 - case constants.WEBSOCKET_REQUEST_REUSE_LAST_CONNECT: - w.reuseLastConnect(d) - default: - logx.Error("未知消息类型:uid:", w.userId, "gid:", w.guestId, "data:", string(data)) + //获取工厂实例 + processor := w.newAllocationProcessor(parseInfo.T) + if processor == nil { + logx.Error("未知消息类型:", string(data)) + return } + //执行工厂方法 + processor.allocationMessage(w, d) } diff --git a/server/websocket/internal/logic/ws_render_image.go b/server/websocket/internal/logic/ws_render_image.go index 887dc2e7..00e7b773 100644 --- a/server/websocket/internal/logic/ws_render_image.go +++ b/server/websocket/internal/logic/ws_render_image.go @@ -18,6 +18,10 @@ import ( "time" ) +// 渲染处理器 +type renderProcessor struct { +} + // 云渲染属性 type extendRenderProperty struct { renderImageTask map[string]*renderTask //需要渲染的图片任务 key是taskId val 是renderId @@ -44,8 +48,8 @@ type renderTask struct { uploadUnityRenderImageTakesTime int64 //上传unity渲染结果图时间 } -// 发送到渲染缓冲队列 -func (w *wsConnectItem) sendToRenderChan(data []byte) { +func (r *renderProcessor) allocationMessage(w *wsConnectItem, data []byte) { + logx.Info("收到渲染任务消息:", string(data)) select { case <-w.closeChan: //已经关闭 return diff --git a/server/websocket/internal/logic/ws_reuse_last_connect.go b/server/websocket/internal/logic/ws_reuse_last_connect.go index 7ef64ec7..33fa2cd7 100644 --- a/server/websocket/internal/logic/ws_reuse_last_connect.go +++ b/server/websocket/internal/logic/ws_reuse_last_connect.go @@ -9,8 +9,11 @@ import ( "github.com/zeromicro/go-zero/core/logx" ) -// 刷新重连请求恢复上次连接的标识 -func (w *wsConnectItem) reuseLastConnect(data []byte) { +// 复用连接处理器 +type reuseConnProcessor struct { +} + +func (r *reuseConnProcessor) allocationMessage(w *wsConnectItem, data []byte) { logx.Info("收到请求恢复上次连接标识数据:", string(data)) var wid string if err := json.Unmarshal(data, &wid); err != nil { @@ -41,7 +44,9 @@ func (w *wsConnectItem) reuseLastConnect(data []byte) { if v, ok := mapConnPool.Load(wid); ok { obj, ok := v.(wsConnectItem) if !ok { + w.reuseLastConnErrResponse("连接断言失败") logx.Error("连接断言失败") + return } //是当前自己占用(无需处理) if obj.uniqueId == w.uniqueId { @@ -54,11 +59,12 @@ func (w *wsConnectItem) reuseLastConnect(data []byte) { } } //重新绑定 + logx.Info("开始重新绑定>>>>>") w.uniqueId = wid mapConnPool.Store(wid, *w) rsp := w.respondDataFormat(constants.WEBSOCKET_CONNECT_SUCCESS, wid) w.sendToOutChan(rsp) - return + logx.Info("重新绑定成功") } // 获取用户拼接部分(复用标识用到)