Merge branch 'develop' of https://gitee.com/fusenpack/fusenapi into develop

This commit is contained in:
eson 2023-08-30 18:44:46 +08:00
commit b73816e687
6 changed files with 219 additions and 145 deletions

View File

@ -2,28 +2,29 @@ package constants
type Websocket string type Websocket string
// websocket消息类型 // websocket消息类型(主类别)
const ( const (
//鉴权失败 WEBSOCKET_UNAUTH Websocket = "WEBSOCKET_UNAUTH" //鉴权失败 1级消息单向通信
WEBSOCKET_UNAUTH Websocket = "WEBSOCKET_UNAUTH" WEBSOCKET_GEN_UNIQUE_ID_ERR Websocket = "WEBSOCKET_GEN_UNIQUE_ID_ERR" //获取ws连接标识错误 1级消息单向通信
//获取ws连接标识错误 WEBSOCKET_CONNECT_SUCCESS Websocket = "WEBSOCKET_CONNECT_SUCCESS" //ws连接成功 1级消息单向通信
WEBSOCKET_GEN_UNIQUE_ID_ERR Websocket = "WEBSOCKET_GEN_UNIQUE_ID_ERR" WEBSOCKET_REQUEST_REUSE_LAST_CONNECT Websocket = "WEBSOCKET_REQUEST_REUSE_LAST_CONNECT" //请求恢复为上次连接的标识 1级消息单向通信
//ws连接成功 WEBSOCKET_REQUEST_RESUME_LAST_CONNECT_ERR Websocket = "WEBSOCKET_REQUEST_RESUME_LAST_CONNECT_ERR" //请求恢复为上次连接的标识错误 1级消息单向通信
WEBSOCKET_CONNECT_SUCCESS Websocket = "WEBSOCKET_CONNECT_SUCCESS" WEBSOCKET_INCOME_CACHE_QUEUE_OVERFLOW = "WEBSOCKET_INCOME_CACHE_QUEUE_OVERFLOW" //数据接收速度超过数据消费速度缓冲队列满了1级消息单向通信
//请求恢复为上次连接的标识 )
WEBSOCKET_REQUEST_REUSE_LAST_CONNECT Websocket = "WEBSOCKET_REQUEST_REUSE_LAST_CONNECT"
//请求恢复为上次连接的标识错误 // websocket消息类型(通用通知类别)
WEBSOCKET_REQUEST_RESUME_LAST_CONNECT_ERR Websocket = "WEBSOCKET_REQUEST_RESUME_LAST_CONNECT_ERR" const (
//图片渲染消息 WEBSOCKET_COMMON_NOTIFY Websocket = "WEBSOCKET_COMMON_NOTIFY" //通用回调通知1级消息单向通信
WEBSOCKET_RENDER_IMAGE Websocket = "WEBSOCKET_RENDER_IMAGE" )
//图片渲染失败消息
WEBSOCKET_RENDER_IMAGE_ERR Websocket = "WEBSOCKET_RENDER_IMAGE_ERR" // websocket消息类型(基本传输结构类别)
//反回合成刀版图消息 const (
WEBSOCKET_COMBINE_IMAGE Websocket = "WEBSOCKET_COMBINE_IMAGE" WEBSOCKET_ERR_DATA_FORMAT Websocket = "WEBSOCKET_ERR_DATA_FORMAT" //传入数据格式错误1级消息单向通信
//传入数据格式错误 )
WEBSOCKET_ERR_DATA_FORMAT Websocket = "WEBSOCKET_ERR_DATA_FORMAT"
//通用回调通知 // websocket消息类型(云渲染类别)
WEBSOCKET_COMMON_NOTIFY Websocket = "WEBSOCKET_COMMON_NOTIFY" const (
//数据接收速度超过数据消费速度(缓冲队列满了) WEBSOCKET_RENDER_IMAGE Websocket = "WEBSOCKET_RENDER_IMAGE" //图片渲染消息(1级消息双向通信)
WEBSOCKET_INCOME_CACHE_QUEUE_OVERFLOW = "WEBSOCKET_INCOME_CACHE_QUEUE_OVERFLOW" WEBSOCKET_RENDER_IMAGE_ERR Websocket = "WEBSOCKET_RENDER_IMAGE_ERR" //图片渲染失败消息1级消息单向通信
WEBSOCKET_COMBINE_IMAGE Websocket = "WEBSOCKET_COMBINE_IMAGE" //反回合成刀版图消息2级消息单向通信属于 WEBSOCKET_RENDER_IMAGE 消息的子流程)
) )

View File

@ -85,117 +85,26 @@ func (l *GetTagProductListLogic) GetTagProductList(req *types.GetTagProductListR
var ( var (
productList []gmodel.FsProduct //产品列表select 字段需要看查询的地方) productList []gmodel.FsProduct //产品列表select 字段需要看查询的地方)
mapTagProp = make(map[int64][]types.CoverDefaultItem) mapTagProp = make(map[int64][]types.CoverDefaultItem)
productOptionalPartList []gmodel.GetGroupPartListByProductIdsRsp //产品配件列表
mapProductHaveOptionFitting = make(map[int64]struct{}) mapProductHaveOptionFitting = make(map[int64]struct{})
productPriceList []gmodel.GetPriceListByProductIdsRsp //产品价格列表select 字段需要看查询的地方) mapProductMinPrice = make(map[int64]int64) //产品最小价格map
mapProductMinPrice = make(map[int64]int64) //产品最小价格map mapProductSizeCount = make(map[int64]int64) //产品尺寸数量map
productTemplatesV2 []gmodel.FsProductTemplateV2 //产品模板列表select 字段需要看查询的地方) mapProductTemplate = make(map[int64]int64) //产品模板map
productSizeCountList []gmodel.CountProductSizeByStatusRsp //产品尺寸数量列表select 字段需要看查询的地方)
mapProductSizeCount = make(map[int64]int64) //产品尺寸数量map
mapProductTemplate = make(map[int64]int64) //产品模板map
) )
//携带产品 //携带产品
if req.WithProduct { if req.WithProduct {
//查询符合的产品列表 productList, err = l.getProductRelationInfo(getProductRelationInfoReq{
pIsDel := int64(0) Ctx: l.ctx,
pStatus := int64(1) TemplateTag: req.TemplateTag,
pIsShelf := int64(1) TypeIds: typeIds,
//获取产品列表 MapTagProp: mapTagProp,
productList, err = l.svcCtx.AllModels.FsProduct.GetProductListByParams(l.ctx, MapProductHaveOptionFitting: mapProductHaveOptionFitting,
gmodel.GetProductListByParamsReq{ MapProductMinPrice: mapProductMinPrice,
Type: typeIds, MapProductSizeCount: mapProductSizeCount,
IsDel: &pIsDel, MapProductTemplate: mapProductTemplate,
IsShelf: &pIsShelf, })
Status: &pStatus,
OrderBy: "`is_recommend` DESC,`sort` ASC",
})
if err != nil { if err != nil {
logx.Error(err) logx.Error(err)
return resp.SetStatusWithMessage(basic.CodeServiceErr, "failed to get product list") return resp.SetStatusWithMessage(basic.CodeServiceErr, "failed to get product series info")
}
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
} }
} }
//map tag菜单 //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菜单数据 // 处理tag菜单数据
type dealWithTagMenuDataReq struct { type dealWithTagMenuDataReq struct {
TagList []gmodel.FsTags TagList []gmodel.FsTags
@ -351,6 +382,7 @@ type getTagProductsReq struct {
User gmodel.FsUser User gmodel.FsUser
} }
// 返回每个标签下的直接从属产品不包含子tag的产品
func (l *GetTagProductListLogic) getTagProducts(req getTagProductsReq) (productListRsp []types.TagProduct) { func (l *GetTagProductListLogic) getTagProducts(req getTagProductsReq) (productListRsp []types.TagProduct) {
//默认给50个容量 //默认给50个容量
productListRsp = make([]types.TagProduct, 0, 50) productListRsp = make([]types.TagProduct, 0, 50)

View File

@ -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
}

View File

@ -313,7 +313,8 @@ func (w *wsConnectItem) consumeInChanData() {
case <-w.closeChan: case <-w.closeChan:
return return
case data := <-w.inChan: 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 var parseInfo websocket_data.DataTransferData
if err := json.Unmarshal(data, &parseInfo); err != nil { if err := json.Unmarshal(data, &parseInfo); err != nil {
w.incomeDataFormatErrResponse("invalid format of income message:" + string(data)) w.incomeDataFormatErrResponse("invalid format of income message:" + string(data))
@ -390,15 +391,12 @@ func (w *wsConnectItem) dealwithReciveData(data []byte) {
return return
} }
d, _ := json.Marshal(parseInfo.D) d, _ := json.Marshal(parseInfo.D)
//分消息类型给到不同逻辑处理,可扩展 //获取工厂实例
switch parseInfo.T { processor := w.newAllocationProcessor(parseInfo.T)
//图片渲染 if processor == nil {
case constants.WEBSOCKET_RENDER_IMAGE: logx.Error("未知消息类型:", string(data))
w.sendToRenderChan(d) return
//刷新重连请求恢复上次连接的标识
case constants.WEBSOCKET_REQUEST_REUSE_LAST_CONNECT:
w.reuseLastConnect(d)
default:
logx.Error("未知消息类型uid:", w.userId, "gid:", w.guestId, "data:", string(data))
} }
//执行工厂方法
processor.allocationMessage(w, d)
} }

View File

@ -18,6 +18,10 @@ import (
"time" "time"
) )
// 渲染处理器
type renderProcessor struct {
}
// 云渲染属性 // 云渲染属性
type extendRenderProperty struct { type extendRenderProperty struct {
renderImageTask map[string]*renderTask //需要渲染的图片任务 key是taskId val 是renderId renderImageTask map[string]*renderTask //需要渲染的图片任务 key是taskId val 是renderId
@ -44,8 +48,8 @@ type renderTask struct {
uploadUnityRenderImageTakesTime int64 //上传unity渲染结果图时间 uploadUnityRenderImageTakesTime int64 //上传unity渲染结果图时间
} }
// 发送到渲染缓冲队列 func (r *renderProcessor) allocationMessage(w *wsConnectItem, data []byte) {
func (w *wsConnectItem) sendToRenderChan(data []byte) { logx.Info("收到渲染任务消息:", string(data))
select { select {
case <-w.closeChan: //已经关闭 case <-w.closeChan: //已经关闭
return return

View File

@ -9,8 +9,11 @@ import (
"github.com/zeromicro/go-zero/core/logx" "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)) logx.Info("收到请求恢复上次连接标识数据:", string(data))
var wid string var wid string
if err := json.Unmarshal(data, &wid); err != nil { 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 { if v, ok := mapConnPool.Load(wid); ok {
obj, ok := v.(wsConnectItem) obj, ok := v.(wsConnectItem)
if !ok { if !ok {
w.reuseLastConnErrResponse("连接断言失败")
logx.Error("连接断言失败") logx.Error("连接断言失败")
return
} }
//是当前自己占用(无需处理) //是当前自己占用(无需处理)
if obj.uniqueId == w.uniqueId { if obj.uniqueId == w.uniqueId {
@ -54,11 +59,12 @@ func (w *wsConnectItem) reuseLastConnect(data []byte) {
} }
} }
//重新绑定 //重新绑定
logx.Info("开始重新绑定>>>>>")
w.uniqueId = wid w.uniqueId = wid
mapConnPool.Store(wid, *w) mapConnPool.Store(wid, *w)
rsp := w.respondDataFormat(constants.WEBSOCKET_CONNECT_SUCCESS, wid) rsp := w.respondDataFormat(constants.WEBSOCKET_CONNECT_SUCCESS, wid)
w.sendToOutChan(rsp) w.sendToOutChan(rsp)
return logx.Info("重新绑定成功")
} }
// 获取用户拼接部分(复用标识用到) // 获取用户拼接部分(复用标识用到)