使用逻辑编排解决阿里云费用账单API集成数据结构差异的问题

简介: 利用流程编排在集成过程中定制阿里云账单API的结构

集成阿里云的费用账单,是公司运维小伙伴经常要承担的工作。阿里云也开放了按照多个维维度查询费用账单的API供企业客户使用,集成到云管平台,财务系统等等。不过在实际工作中,常常会发现需要集成的云供应商不止一家,每一家提供的API格式都不一样。如果想要接入已经开发好的系统,数据结构不一致就需要做额外的数据Mapping工作,费时费力。小编在阿里云上发现了一款神器,可对阿里云提供的API做快速的定制,输出想要的格式,这就是阿里云的逻辑编排。

什么是逻辑编排?

举个实际的例子,阿里云账单的API QueryInstanceBill可返回按照实例维度或者计费项维度的账单,返回的格式是这样的:
"Data": {

"Items": {
  "Item": [
    {
                "SubscriptionType": "PayAsYouGo",
                "ProductCode": "cdn",
                "RecordID": "2019080352963162",
                "BillingItem": "InlandNetworkOut",
                "ProductDetail": "cdn",
                "DeductedByPrepaidCard": 0,
                "PretaxAmount": 0,
                "DeductedByCoupons": 0,
                "RoundDownDiscount": 0,
                "UsageStartTime": "2019-08-08 12:00:00",
                "UsageEndTime": "2019-08-08 13:00:00",
                "Status": "NoSettle",
                "PaymentTime": "",
                "PaymentAmount": 0,
                "Item": "PayAsYouGoBill",
                "OutstandingAmount": 0,
                "ProductType": "",
                "DeductedByCashCoupons": 0,
                "OwnerID": "",
                "ProductName": "cdn",
                "Currency": "CNY",
                "PretaxGrossAmount": 0,
                "InvoiceDiscount": 0
            }
            ]

},

如果在集成的过程中,仅希望InlandNetworkOut这一个计费项的用量和金额汇总,输出一个特定Json格式的结果,可以按照下面的步骤操作。

第一步 权限设置

逻辑编排过程中需要调用QueryInstanceBill接口,因此需要相关的权限。可以使用AliyunBSSFullAccess权限,但为了保证最小可用原则,创建一个新的权限策略AliyunBSSBillReadOnly,使用以下的Statement
{
"Version": "1",
"Statement": [

   {
       "Action": ["bss:Query*", "bss:Describe*"],
       "Resource": "*",
       "Effect": "Allow"
   }

]
}
1579513892267_a3ab1069_ff7f_44e0_b839_8b0389f87636
同时创建一个拥有此权限Ram角色,并添加以下的信任策略:

  1. 创建RAM Role,选择Alibaba Cloud Service类型
  2. 添加刚刚创建的策略AliyunBSSBillReadOnly
  3. 修改信任策略,让逻辑编排可以访问
    {

    "Statement": [
        {
            "Action": "sts:AssumeRole",
            "Effect": "Allow",
            "Principal": {
                "Service": [
                    "composer.aliyuncs.com"
                ]
            }
        }
    ],
    "Version": "1"

    }

1579514503818_9c76a9d5_1f66_448a_b251_07f0ea338919

第二步 编排流程
登录国际站并访问逻辑编排控制台https://lc.console.aliyun.com/,在左侧的菜单选择“我的工作流”,然后创建一个新的流程,输入名称和描述。创建完成后,转到“代码设计”,然后复制本文给出的一个段脚本。
{

"actions": {
    "查询_InstanceBill_第一页数据": {
        "type": "ACS::BSS::QueryInstanceBill",
        "inputs": {
            "method": "POST",
            "host": {
                "product": "BSS",
                "api": "QueryInstanceBill",
                "apiVersion": "2017-12-14"
            },
            "connection": {},
            "queries": {
                "RegionId": "ap-southeast-1",
                "BillingCycle": "@get(triggerOutputs(), 'queries.BillingCycle')",
                "ProductCode": "cdn",
                "PageNum": 1,
                "PageSize": 300,
                "IsBillingItem": true
            }
        },
        "runAfter": {
            "初始化变量存放所有记录": [
                "Succeeded"
            ]
        }
    },
    "初始化变量存放所有记录": {
        "type": "InitializeVariable",
        "inputs": {
            "name": "records",
            "type": "Array",
            "value": []
        },
        "runAfter": {}
    },
    "存入第一页数据": {
        "type": "AppendToArrayVariable",
        "inputs": {
            "name": "records",
            "value": "@body('查询_InstanceBill_第一页数据')['Data']['Items']['Item']"
        },
        "runAfter": {
            "查询_InstanceBill_第一页数据": [
                "Succeeded"
            ]
        }
    },
    "遍历剩余页数": {
        "type": "Until",
        "inputs": {},
        "runAfter": {
            "初始化变量存放当前页数": [
                "Succeeded"
            ]
        },
        "actions": {
            "查询某一页_InstanceBill_数据": {
                "type": "ACS::BSS::QueryInstanceBill",
                "inputs": {
                    "method": "POST",
                    "host": {
                        "product": "BSS",
                        "api": "QueryInstanceBill",
                        "apiVersion": "2017-12-14"
                    },
                    "connection": {},
                    "queries": {
                        "BillingCycle": "@get(triggerOutputs(), 'queries.BillingCycle')",
                        "RegionId": "ap-southeast-1",
                        "ProductCode": "cdn",
                        "PageNum": "@variables('pageNumber')",
                        "PageSize": 300,
                        "IsBillingItem": true
                    }
                },
                "runAfter": {
                    "设置页数": [
                        "Succeeded"
                    ]
                }
            },
            "追加值至数组变量": {
                "type": "AppendToArrayVariable",
                "inputs": {
                    "name": "records",
                    "value": "@body('查询某一页_InstanceBill_数据')['Data']['Items']['Item']"
                },
                "runAfter": {
                    "查询某一页_InstanceBill_数据": [
                        "Succeeded"
                    ]
                }
            },
            "设置页数": {
                "runAfter": {},
                "type": "SetVariable",
                "inputs": {
                    "name": "pageNumber",
                    "value": "@add(variables('pageNumber'), 1)"
                }
            }
        },
        "expression": "@lte(sub(ceil(div(body('查询_InstanceBill_第一页数据').Data.TotalCount, 300)), variables('pageNumber')), 0)"
    },
    "初始化变量存放当前页数": {
        "type": "InitializeVariable",
        "inputs": {
            "name": "pageNumber",
            "type": "Integer",
            "value": 1
        },
        "runAfter": {
            "存入第一页数据": [
                "Succeeded"
            ]
        }
    },
    "计算总数": {
        "type": "JavascriptCode",
        "inputs": {
            "code": "const records = $context.variables('records');\n\nconst allUsage = records.reduce((sum, group) => {\n    if (Array.isArray(group)) {\n        group.forEach((record) => {\n            if (record.BillingItem === 'InlandNetworkOut') {\n                if (record.Usage) {\n                    sum.Usage += parseFloat(record.Usage) || 0;\n                }\n                if (record.PretaxAmount) {\n                    sum.PretaxAmount += parseFloat(record.PretaxAmount) || 0;\n                }\n            }\n            \n        });\n    } else if (group.Usage || group.PretaxAmount) {\n        if (group.BillingItem === 'InlandNetworkOut') {\n            sum.Usage += parseFloat(group.Usage) || 0;\n            sum.PretaxAmount += parseFloat(group.PretaxAmount) || 0;\n        }\n    }\n    \n    return sum;\n}, { Usage: 0, PretaxAmount: 0 });\n\nreturn allUsage;"
        },
        "runAfter": {
            "遍历剩余页数": [
                "Succeeded"
            ]
        }
    },
    "响应": {
        "type": "Response",
        "outputs": {
            "statusCode": 500,
            "body": {                  
                "cost_data": [
                    {                        
                        "usage": "@body('计算总数').Usage",
                        "cost": "@body('计算总数').PretaxAmount"
                    }
                ]
            },
            "headers": {
                "Content-Type": "application/json"
            }
        },
        "runAfter": {
            "计算总数": [
                "Succeeded"
            ]
        }
    }
}

}

返回图形化设置,这时需要对其中调用API的过程进行授权。点击并展开“查询InstanceBill第一页数据”,在下方点击Change Permissions,添加刚刚创建新的RAM Role,注意Authorized Policies是否包括了应该有的权限。再点开“遍历剩余页数”这一步,再展开“查询某一页InstanceBill数据”,进行如上的权限添加步骤。点击右上角的保存按键保存流程。

第三步 调用流程
在流程的第一步中,可以找到服务发起的URL,Copy到浏览器中可进行测试。在上面的例子中,调用时需要添加一个查询账期的参数BillingCycle=YYYY-MM。根据所需,可更改调用方式为POST或者GET。

这就是一个利用流程编改变API结构的方式,希望能够帮助到您。

相关文章
|
10月前
|
API 微服务
阿里云微服务引擎 MSE 及 API 网关 2025 年 9 月产品动态
阿里云微服务引擎 MSE 及 API 网关 2025 年 9 月产品动态。
1051 69
|
11月前
|
运维 Cloud Native 应用服务中间件
阿里云微服务引擎 MSE 及 API 网关 2025 年 9 月产品动态
阿里云微服务引擎 MSE 面向业界主流开源微服务项目, 提供注册配置中心和分布式协调(原生支持 Nacos/ZooKeeper/Eureka )、云原生网关(原生支持Higress/Nginx/Envoy,遵循Ingress标准)、微服务治理(原生支持 Spring Cloud/Dubbo/Sentinel,遵循 OpenSergo 服务治理规范)能力。API 网关 (API Gateway),提供 APl 托管服务,覆盖设计、开发、测试、发布、售卖、运维监测、安全管控、下线等 API 生命周期阶段。帮助您快速构建以 API 为核心的系统架构.满足新技术引入、系统集成、业务中台等诸多场景需要。
687 142
|
11月前
|
人工智能 自然语言处理 安全
探索 API 的无限可能:阿里云的卓越引领
API是数字化时代的“万能钥匙”,阿里云凭借云控制API、API网关、OpenAPI Explorer等产品,构建全方位API生态,助力企业高效集成、安全管控与创新升级,推动电商、制造等行业智能化转型,引领未来科技浪潮。
424 10
|
11月前
|
人工智能 运维 监控
阿里云 API 聚合实战:破解接口碎片化难题,3 类场景方案让业务响应提速 60%
API聚合破解接口碎片化困局,助力开发者降本增效。通过统一中间层整合微服务、第三方接口与AI模型,实现调用次数减少60%、响应提速70%。阿里云实测:APISIX+函数计算+ARMS监控组合,支撑百万级并发,故障定位效率提升90%。
807 0
|
11月前
|
人工智能 API 监控
告别多接口拼凑!阿里云 API 模型聚合实现技术能力协同跃迁
API聚合整合400+国内外AI模型,统一接口、屏蔽差异,降低开发与维护成本,提升效率与系统稳定性,助力开发者高效应对多API调用困境。
1192 0
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
1374 10
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
465 59
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
1209 77
|
编译器 C语言 C++
栈区的非法访问导致的死循环(x64)
这段内容主要分析了一段C语言代码在VS2022中形成死循环的原因,涉及栈区内存布局和数组越界问题。代码中`arr[15]`越界访问,修改了变量`i`的值,导致`for`循环条件始终为真,形成死循环。原因是VS2022栈区从低地址到高地址分配内存,`arr`数组与`i`相邻,`arr[15]`恰好覆盖`i`的地址。而在VS2019中,栈区先分配高地址再分配低地址,因此相同代码表现不同。这说明编译器对栈区内存分配顺序的实现差异会导致程序行为不一致,需避免数组越界以确保代码健壮性。
305 0
栈区的非法访问导致的死循环(x64)
232.用栈实现队列,225. 用队列实现栈
在232题中,通过两个栈(`stIn`和`stOut`)模拟队列的先入先出(FIFO)行为。`push`操作将元素压入`stIn`,`pop`和`peek`操作则通过将`stIn`的元素转移到`stOut`来实现队列的顺序访问。 225题则是利用单个队列(`que`)模拟栈的后入先出(LIFO)特性。通过多次调整队列头部元素的位置,确保弹出顺序符合栈的要求。`top`操作直接返回队列尾部元素,`empty`判断队列是否为空。 两题均仅使用基础数据结构操作,展示了栈与队列之间的转换逻辑。