【背景】
Google billing 账单系统登录后可查看到当前项目所关联的账单报告及明细,但是某客户领导由于网络的问题,并不能随时登录BIlling中查看账单,并提出希望在公司常用的办公软件飞书中收到billing的使用情况。
【设计】
根据客户需求,在通过了解Google billing的官方文档及相关的接口情况后 ,我们设计了如下的数据流程图及本次需求开发的架构图:
【实操】
在billing中创建Budget Alert并关联到Pub/Sub主题中,通过Pub/Sub的订阅功能将billing的账单信息通过Cluod function推送至第三方
1.创建Pub/sub主题
2.创建Budget Alert,创建时需注意以下四点:
a.Budget alert名
b.预算额度
c.预算报警的阙值
d.勾选Pub/Sub选项并选择创建的Pub/Sub
创建后可在控制台检查
3.添加Pub/sub触发器,并将开发的代码copy上去,修改入口函数,然后点击部署函数即可
【代码】
// Package p contains a Pub/Sub Cloud Function.
package p
import (
"bytes"
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"golang.org/x/net/context"
"log"
"net/http"
"sync"
"time"
)
var msgLocker sync.Mutex
var (
contentType = "Content-Type:application/json"
webHooker = ""//这里填飞书的地址
secret = "" // 这里填签名,可不填
)
//定义接受信息的类型
type PubSubMessage struct {
Data []byte `json:"data"`
BudgetIdstring `json:"budgetId"`
BillingAccountIdstring `json:"billingAccountId"`
}
//定义信息内容
type Message struct {
BudgetDisplayName string `json:"budget_display_name"`
CostAmount float64 `json:"costAmount"`
CostIntervalStart string `json:"costIntervalStart"`
BudgetAmountTypestring `json:"budget_amount_type"`
BudgetAmount float64 `json:"budgetAmount"`
AlertThresholdExceededfloat64`json:"alertThresholdExceeded"`
ForecastThresholdExceededfloat64`json:"forecast_threshold_exceeded"`
CurrencyCodestring `json:"currency_code"`
}
//发送验证并消息的Function
func SendMsg(ctx context.Context, m PubSubMessage) error {
fmt.Println(m)
msgLocker.Lock()
defer msgLocker.Unlock()
var messageMessage
errJson:= json.Unmarshal(m.Data, &message)
if errJson!= nil {
fmt.Println("接收信息错误",errJson,string(m.Data))
return errors.New("接收信息错误")
}
content := fmt.Sprintf(
"账单ID:%v,"+"\n"+
"预算ID:%v,"+"\n"+
"使用的费用:%v," +"\n"+
"预算开始时间:%v," +"\n"+
"预算额度:%v" +"\n",
m.BillingAccountId,
m.BudgetId,
message.CostAmount,
message.CostIntervalStart,
message.BudgetAmount)
var data = make(map[string]interface{})
data["msg_type"] = "text"
data["content"] = map[string]string{
"text":content,
}
timestamp := time.Now().Unix()
if secret!= "" {
sign, _ := GenSign(timestamp, secret)
data["sign"] =sign
}
data["timestamp"] =timestamp
d, _ := json.Marshal(data)
_, err := http.Post(webHooker, contentType, bytes.NewBuffer(d))
if err != nil {
log.Println("消息发送失败,HTTP connection error!")
return errors.New("HTTPconnection error!")
}
returnnil
}
func GenSign(timestamp int64, secret string) (string, error) {
stringToSign := fmt.Sprintf("%v", timestamp)+ "\n" + secret
var data []byte
h := hmac.New(sha256.New, []byte(stringToSign))
_, err :=h.Write(data)
if err != nil {
return "", err
}
signature := base64.StdEncoding.EncodeToString(h.Sum(nil))
return signature, nil
}