前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >怎样使用 Go 写 “递归 + 协程” 代码?

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

作者头像
一个会写诗的程序员
发布2022-09-28 09:11:18
2060
发布2022-09-28 09:11:18
举报
代码语言: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
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-09-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云直播
云直播(Cloud Streaming Services,CSS)为您提供极速、稳定、专业的云端直播处理服务,根据业务的不同直播场景需求,云直播提供了标准直播、快直播、云导播台三种服务,分别针对大规模实时观看、超低延时直播、便捷云端导播的场景,配合腾讯云视立方·直播 SDK,为您提供一站式的音视频直播解决方案。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档