Files
LoganandClaude 76ec9387f0 feat(rdp): 新增 RDP 远程桌面/SMB/账单同步能力 + 清理编译产物
主要变更:
- RDP 远程桌面:新增 backend/pkg/rdp(RDP 引擎 + credssp/display/rle/trace)、
  backend/controller/rdp*(连接/凭证/文件/会话/实时)、frontend RDP 组件与 API
- Windows SMB 文件传输:backend/pkg/winsmb
- 云账单同步:backend/pkg/billingsync + billing_sync/aliyun_billing,前端账单详情
- Agent CLI Kubernetes 工具:kubernetes_action/get、pod_execute/logs
- 服务器导入助手:server_import_helpers、ImportCredentialSettings
- 国际化与组件重构(cloud/deploy/monitor/server/system 等多模块)
- 清理编译产物二进制:移除 backend/server、backend/agent/bin/aiops-agent-*
- .gitignore:新增 backend/agent/ 与 .cache/(避免 334MB go-build 缓存误入库)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-04 01:50:36 +08:00

141 lines
3.3 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package cloud
import (
"encoding/json"
"fmt"
"math"
"strconv"
"strings"
)
// FetchDailyBill 拉取指定日期的阿里云日账单(按产品+地域聚合) // Logan.Li
func (a *AliyunProvider) FetchDailyBill(billingDate string) ([]CloudBillingRecord, error) {
if len(billingDate) != 10 {
return nil, fmt.Errorf("账单日期格式应为 YYYY-MM-DD")
}
billingCycle := billingDate[:7]
type aggKey struct {
service string
region string
}
agg := map[aggKey]float64{}
currency := "CNY"
pageNum := 1
pageSize := 300
totalCount := -1
for {
body, err := a.callBSS("QueryInstanceBill", map[string]string{
"BillingCycle": billingCycle,
"BillingDate": billingDate,
"Granularity": "DAILY",
"PageNum": strconv.Itoa(pageNum),
"PageSize": strconv.Itoa(pageSize),
"IsHideZeroCharge": "true",
})
if err != nil {
return nil, err
}
var resp aliyunInstanceBillResponse
if err := json.Unmarshal(body, &resp); err != nil {
return nil, fmt.Errorf("解析账单响应失败: %w", err)
}
if !resp.Success && resp.Code != "Success" {
msg := resp.Message
if msg == "" {
msg = string(body)
}
return nil, fmt.Errorf("阿里云账单 API 错误: %s", msg)
}
if totalCount < 0 {
totalCount = resp.Data.TotalCount
}
items := normalizeBillItems(resp.Data.Items.Item)
for _, it := range items {
amount := it.PretaxAmount
if amount <= 0 {
amount = it.PaymentAmount
}
if amount <= 0 {
continue
}
service := strings.TrimSpace(it.ProductName)
if service == "" {
service = strings.TrimSpace(it.ProductCode)
}
if service == "" {
service = "其他"
}
region := strings.TrimSpace(it.Region)
if region == "" {
region = "全局"
}
key := aggKey{service: service, region: region}
agg[key] += amount
if it.Currency != "" {
currency = it.Currency
}
}
if len(items) == 0 || pageNum*pageSize >= totalCount {
break
}
pageNum++
}
records := make([]CloudBillingRecord, 0, len(agg))
for key, cost := range agg {
records = append(records, CloudBillingRecord{
BillingDate: billingDate,
Provider: "aliyun",
Cost: math.Round(cost*100) / 100,
Currency: currency,
Service: key.service,
Region: key.region,
Description: "云API自动同步(阿里云 QueryInstanceBill)",
})
}
return records, nil
}
type aliyunInstanceBillResponse struct {
Code string `json:"Code"`
Message string `json:"Message"`
Success bool `json:"Success"`
Data struct {
TotalCount int `json:"TotalCount"`
Items struct {
Item json.RawMessage `json:"Item"`
} `json:"Items"`
} `json:"Data"`
}
type aliyunBillItem struct {
ProductCode string `json:"ProductCode"`
ProductName string `json:"ProductName"`
Region string `json:"Region"`
PretaxAmount float64 `json:"PretaxAmount"`
PaymentAmount float64 `json:"PaymentAmount"`
Currency string `json:"Currency"`
}
// normalizeBillItems 兼容 Item 为对象或数组两种返回格式 // Logan.Li
func normalizeBillItems(raw json.RawMessage) []aliyunBillItem {
if len(raw) == 0 || string(raw) == "null" {
return nil
}
var items []aliyunBillItem
if err := json.Unmarshal(raw, &items); err == nil {
return items
}
var one aliyunBillItem
if err := json.Unmarshal(raw, &one); err == nil {
return []aliyunBillItem{one}
}
return nil
}