美文网首页
怎样使用 Go 写 “递归 + 协程” 代码?

怎样使用 Go 写 “递归 + 协程” 代码?

作者: 光剑书架上的书 | 来源:发表于2022-09-20 12:08 被阅读0次
    func (rcvr *ProductOverviewDownloadCateService) executeDownload(ctx context.Context,
        req *common.GeneralizedCallForRpcReq,
        cateIndexDataRequest product_overview.ProductOverviewCateIndexDataByParentCateIdRequest,
        err error,
        h *ProductOverviewDownloadCateService,
        resp *common.GeneralizedCallForRpcResp,
        excelTableData [][]string) (*product_overview.ProductOverviewCateIndexDataResponse, *common.GeneralizedCallForRpcResp, error, bool, [][]string) {
    
        logu.CtxInfo(ctx, "ProductOverviewDownloadCate", "ProductOverviewGetCategoryIndexDataListByParentCateIdService.ProductOverviewGetCategoryIndexDataListByParentCateId,cateIndexDataRequest=%v", convert.ToJSONString(cateIndexDataRequest))
        fisrtLevelCateDataIndexResp, err := h.ProductOverviewGetCategoryIndexDataListByParentCateIdService.ProductOverviewGetCategoryIndexDataListByParentCateId(ctx, &cateIndexDataRequest, false)
    
        if err != nil {
            logu.CtxError(ctx, error_code.ParamError, "ProductOverviewDownloadCate", "ProductOverviewGetCategoryIndexDataListByParentCateId,cateIndexDataRequest=%v,err=%v", convert.ToJSONString(cateIndexDataRequest), err)
            return nil, resp, err, true, excelTableData
        }
    
        logu.CtxInfo(ctx, "ProductOverviewDownloadCate,", "ProductOverviewGetCategoryIndexDataListByParentCateIdService.ProductOverviewGetCategoryIndexDataListByParentCateId, fisrtLevelCateDataIndexResp=%v", convert.ToJSONString(fisrtLevelCateDataIndexResp))
    
        if fisrtLevelCateDataIndexResp.Data == nil || len(fisrtLevelCateDataIndexResp.Data.DataResult_) == 0 {
            return nil, resp, err, true, excelTableData
        }
    
        // 声明一个带“读写锁 + map ” 的组合结构体
        cateDataIndexMapSafe := struct {
            sync.RWMutex
            cateDataIndexMap map[int32]*product_overview.ProductOverviewCateIndexDataResponse
        }{cateDataIndexMap: make(map[int32]*product_overview.ProductOverviewCateIndexDataResponse, 0)}
    
        // 一级类目指标数据
        cateDataIndexMapSafe.cateDataIndexMap[0] = fisrtLevelCateDataIndexResp // 一级类目指标 parentId=0
        // 递归获取当前页所有子类目指标数据
        h.parallelGetCateDataIndex(ctx, &cateDataIndexMapSafe, cateIndexDataRequest)
    
        logu.CtxInfo(ctx, "parallelGetCateDataIndex", "递归获取当前页所有子类目指标数据结果 cateDataIndexMap=%v", convert.ToJSONString(cateDataIndexMapSafe))
    
        // 递归组装返回结果
        excelTableData = h.buildExcelTableData(ctx, excelTableData, cateDataIndexMapSafe.cateDataIndexMap, config.DefaultParentIdInt)
    
        logu.CtxInfo(ctx, "downloadCateStatsData", "cateIndexDataRequest=%v,excelTableData=%v", convert.ToJSONString(cateIndexDataRequest), convert.ToJSONString(excelTableData))
    
        return fisrtLevelCateDataIndexResp, nil, nil, false, excelTableData
    }
    
    // 注意:cateIndexDataRequest 的传值方式,协程递归循环的时候,小心使用指针,入参推荐使用值传递.
    func (h *ProductOverviewDownloadCateService) parallelGetCateDataIndex(
        ctx context.Context,
        cateDataIndexMapSafe *struct {
            sync.RWMutex
            cateDataIndexMap map[int32]*product_overview.ProductOverviewCateIndexDataResponse
        },
        cateIndexDataRequest product_overview.ProductOverviewCateIndexDataByParentCateIdRequest) {
    
        // 父节点ID
        parentCateId := *cateIndexDataRequest.ParentCateId
        var cateDataIndexResp *product_overview.ProductOverviewCateIndexDataResponse
        // 在协程操作map的过程中,进行map的读操作,也需要加个锁:
        cateDataIndexMapSafe.RLock()
        cateDataIndexResp = (cateDataIndexMapSafe.cateDataIndexMap)[parentCateId]
        cateDataIndexMapSafe.RUnlock()
    
        logu.CtxInfo(ctx, "ProductOverviewDownloadCate,", "parallelGetCateDataIndex, parentCateId=%v, cateIndexDataRequest=%v,cateDataIndexResp=%v", parentCateId, convert.ToJSONString(cateIndexDataRequest), convert.ToJSONString(cateDataIndexResp))
    
        // 递归结束的判断
        if cateDataIndexResp == nil || cateDataIndexResp.Data == nil || len(cateDataIndexResp.Data.DataResult_) == 0 {
            return
        }
    
        // 开启协程获取
        var fns []func()
    
        for _, parentCateDataIndex := range cateDataIndexResp.Data.DataResult_ {
            // 临时变量 cateDataStats
            cateDataStats := parentCateDataIndex
    
            fns = append(fns, func() {
                // 作为下一次递归的父节点ID
                currentParentCateId := cateDataStats.CateId
                // 这里,要使用值传递
                cateIndexDataRequestSub := product_overview.ProductOverviewCateIndexDataByParentCateIdRequest{
                    DateType:  cateIndexDataRequest.DateType,
                    BeginDate: cateIndexDataRequest.BeginDate,
                    EndDate:   cateIndexDataRequest.EndDate,
                    // 店铺ID
                    ShopId: cateIndexDataRequest.ShopId,
                    // 设置子类目列表的父节点ID
                    ParentCateId: convert.ToInt32Ptr(currentParentCateId),
                    SortField:    cateIndexDataRequest.SortField,
                    IsAsc:        cateIndexDataRequest.IsAsc,
                    PageNo:       cateIndexDataRequest.PageNo,
                    PageSize:     cateIndexDataRequest.PageSize,
                }
    
                logu.CtxInfo(ctx, "ProductOverviewDownloadCate", "获取子级类目指标请求 ProductOverviewGetCategoryIndexDataListByParentCateId,cateIndexDataRequestSub=%v", convert.ToJSONString(cateIndexDataRequestSub))
                // 获取子级类目指标
                subProductListResp, err := h.ProductOverviewGetCategoryIndexDataListByParentCateIdService.ProductOverviewGetCategoryIndexDataListByParentCateId(ctx, &cateIndexDataRequestSub, false)
    
                if err != nil {
                    logu.CtxError(ctx, error_code.ProcessError, "ProductOverviewDownloadCate", "开启协程获取 ProductOverviewGetCategoryIndexDataListByParentCateId cateIndexDataRequestSub==%v,err=%v", convert.ToJSONString(cateIndexDataRequestSub), err)
                    return
                }
    
                logu.CtxInfo(ctx, "ProductOverviewDownloadCate", "获取子级类目指标返回 ProductOverviewGetCategoryIndexDataListByParentCateId,subProductListResp=%v", convert.ToJSONString(subProductListResp))
    
                // currentParentCateId 指标值存储到 cateDataIndexMap
                cateDataIndexMapSafe.Lock()
                (cateDataIndexMapSafe.cateDataIndexMap)[currentParentCateId] = subProductListResp
                cateDataIndexMapSafe.Unlock()
    
                // 递归获取 subProductListResp
                h.parallelGetCateDataIndex(ctx, cateDataIndexMapSafe, cateIndexDataRequestSub)
            })
        }
    
        // go 协程并行执行
        stream.Parallel(fns...)
    
    }
    
    /*
        "一级品类",
        "二级品类",
        "三级品类",
        "四级品类",
        "品类名称",
        "曝光次数",
        "点击次数",
        "成交订单数",
        "成交金额",
        "成交件数",
        "件单价",
        "直播间成交金额",
        "短视频成交金额",
        "退款金额",
        "曝光点击率",
        "点击成交转化率",
        "千次曝光成交金额",
        "成交人数",
        "成交新客数",
    */
    func (h *ProductOverviewDownloadCateService) buildExcelTableData(ctx context.Context, excelTableData [][]string, cateDataIndexMap map[int32]*product_overview.ProductOverviewCateIndexDataResponse, parentId int32) [][]string {
        logu.CtxInfo(ctx, "ProductOverviewDownloadCate", "buildExcelTableData parentId=%v,excelTableData=%v", parentId, convert.ToJSONString(excelTableData))
        // 如果 cateDataIndexMap 中不存在 parentId 值,直接返回
        if _, exist := cateDataIndexMap[parentId]; !exist {
            return excelTableData
        }
    
        // 取出 parentId 对应的列表值
        cateDataIndexResp := cateDataIndexMap[parentId]
        // 递归结束的判断
        if cateDataIndexResp.Data == nil || len(cateDataIndexResp.Data.DataResult_) == 0 {
            return excelTableData
        }
    
        for _, cateData := range cateDataIndexResp.Data.DataResult_ {
            // 组装 Excel 一行数据
            excelTableData = append(excelTableData, []string{
                cateData.CateNameLevel1, //一级品类
                cateData.CateNameLevel2, //二级品类
                cateData.CateNameLevel3, //三级品类
                cateData.CateNameLevel4, //四级品类
                cateData.CateName,       // 品类名称
                download_util.GetElementStrByIndexInfo(cateData.ProductShowCnt),  //"曝光次数",
                download_util.GetElementStrByIndexInfo(cateData.ProductClickCnt), //"点击次数",
                download_util.GetElementStrByIndexInfo(cateData.PayCnt),          //"成交订单数",
                download_util.GetElementStrByIndexInfo(cateData.PayAmt),          //"成交金额",
                download_util.GetElementStrByIndexInfo(cateData.PayComboCnt),     //"成交件数",
                download_util.GetElementStrByIndexInfo(cateData.AvgPrice),        //"件单价",
                download_util.GetElementStrByIndexInfo(cateData.LivePayAmt),      //"直播间成交金额",
                download_util.GetElementStrByIndexInfo(cateData.VideoPayAmt),     //"短视频成交金额",
                download_util.GetElementStrByIndexInfo(cateData.RefundAmt),       //"退款金额",
                download_util.GetElementStrByIndexInfo(cateData.ClickShowRatio),  //"曝光点击率",
                download_util.GetElementStrByIndexInfo(cateData.PayClickRatio),   //"点击成交转化率",
                download_util.GetElementStrByIndexInfo(cateData.Gpm),             //"千次曝光成交金额",
                download_util.GetElementStrByIndexInfo(cateData.PayUcnt),         //"成交人数",
                //download_util.GetElementStrByIndexInfo(cateData.PayUcntNew),            //"成交新客数",
            })
    
            // 递归子类目ID指标
            excelTableData = h.buildExcelTableData(ctx, excelTableData, cateDataIndexMap, cateData.CateId)
        }
    
        return excelTableData
    }
    
    

    相关文章

      网友评论

          本文标题:怎样使用 Go 写 “递归 + 协程” 代码?

          本文链接:https://www.haomeiwen.com/subject/hjxrgrtx.html