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

2022-09-28 09:11:18 浏览数 (1)

代码语言:javascript复制
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
}

0 人点赞