chore: 统一本地服务器与远程仓库源码

- 合并 main 与 master 历史并以服务器结构为基线
- 保留迁移、数据保留、AWS 账单与部署安全能力
- 建立 SDD 规格和 CI 验证闭环
This commit is contained in:
Logan.Li committed 2026-08-05 20:03:25 +08:00
commit fd60361d93
460 files changed
+37492 -23871

No files matched your search

+80 -23
View File
@@ -1,45 +1,102 @@
package controller
import (
"encoding/json"
"net/http/httptest"
"testing"
"time"
"ezops/config"
"ezops/model"
"github.com/gin-gonic/gin"
"github.com/glebarez/sqlite"
"gorm.io/gorm"
)
// TestBillingDimensionAttribution 验证费用变化额和贡献度使用数据库完整数据计算。 // Logan.Li
func TestBillingDimensionAttribution(t *testing.T) {
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
if err != nil {
t.Fatalf("打开测试数据库失败: %v", err)
// TestCurrencyExpression 验证 CNY 与 USD 报告币种使用同一组可逆汇率。 // Logan.Li
func TestCurrencyExpression(t *testing.T) {
handler := NewBillingHandler(nil, &config.BillingConfig{
BaseCurrency: "CNY",
ExchangeRates: map[string]float64{"cny": 1, "usd": 7.2},
})
_, cnyArgs, ok := handler.currencyExpression("CNY")
if !ok || len(cnyArgs) != 2 {
t.Fatalf("CNY 汇率表达式无效: ok=%v args=%v", ok, cnyArgs)
}
if err = db.AutoMigrate(&model.CloudBilling{}); err != nil {
t.Fatalf("迁移账单表失败: %v", err)
metadata := handler.reportCurrencyMetadata("USD")
rates := metadata["exchange_rates"].(map[string]float64)
if rates["USD"] != 1 || rates["CNY"] != 0.138889 {
t.Fatalf("USD 折算汇率错误: %+v", rates)
}
if _, _, supported := handler.currencyExpression("EUR"); supported {
t.Fatal("未配置币种不应被接受")
}
}
// TestBillingCurrencyConversionAPI 验证统计接口不会直接混加不同原币金额。 // Logan.Li
func TestBillingCurrencyConversionAPI(t *testing.T) {
gin.SetMode(gin.TestMode)
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
if err != nil {
t.Fatal(err)
}
if err := db.AutoMigrate(&model.CloudBilling{}, &model.BillingAlertRule{}); err != nil {
t.Fatal(err)
}
today := time.Now().Format("2006-01-02")
records := []model.CloudBilling{
{BillingDate: "2026-06-01", Provider: "tencent", Service: "CVM", Region: "广州", AccountName: "预发", Currency: "CNY", Cost: 100},
{BillingDate: "2026-07-01", Provider: "tencent", Service: "CVM", Region: "广州", AccountName: "预发", Currency: "CNY", Cost: 180},
{BillingDate: "2026-07-01", Provider: "tencent", Service: "COS", Region: "广州", AccountName: "预发", Currency: "CNY", Cost: 20},
{Provider: "tencent", BillingDate: today, Cost: 100, Currency: "CNY", Service: "CVM"},
{Provider: "aws", BillingDate: today, Cost: 10, Currency: "USD", Service: "EC2"},
}
if err = db.Create(&records).Error; err != nil {
t.Fatalf("写入测试账单失败: %v", err)
if err := db.Create(&records).Error; err != nil {
t.Fatal(err)
}
handler := NewBillingHandler(db, &config.BillingConfig{BaseCurrency: "CNY", ExchangeRates: map[string]float64{"CNY": 1, "USD": 7.2}})
handler := NewBillingHandler(db)
items := handler.billingDimensionAttribution("service", "2026-06-01", "2026-06-30", "2026-07-01", "2026-07-31", "CNY", 100)
if len(items) != 2 {
t.Fatalf("归因项数量错误: got=%d want=2", len(items))
assertMonthCost := func(currency string, expected float64) {
t.Helper()
recorder := httptest.NewRecorder()
context, _ := gin.CreateTestContext(recorder)
context.Request = httptest.NewRequest("GET", "/?currency="+currency, nil)
handler.GetBillingStats(context)
var response struct {
Code int `json:"code"`
Data struct {
MonthCost float64 `json:"month_cost"`
} `json:"data"`
}
if err := json.Unmarshal(recorder.Body.Bytes(), &response); err != nil {
t.Fatal(err)
}
if response.Code != 200 || mathRound(response.Data.MonthCost, 2) != expected {
t.Fatalf("%s 月费用错误: code=%d cost=%f", currency, response.Code, response.Data.MonthCost)
}
}
result := map[string]billingAttribution{}
for _, item := range items {
result[item.Name] = item
assertMonthCost("CNY", 172)
assertMonthCost("USD", 23.89)
}
// TestBuildBillingAnalysisMonths 验证空月份补齐和非 Top 分类合并。 // Logan.Li
func TestBuildBillingAnalysisMonths(t *testing.T) {
start, _ := time.Parse("2006-01", "2026-03")
end, _ := time.Parse("2006-01", "2026-04")
costs := map[string]map[string]float64{
"2026-03": {"CVM": 100, "TCR": 20},
}
months := buildBillingAnalysisMonths(start, end, costs, []string{"CVM", "其他"})
if len(months) != 2 || months[0].Total != 120 || months[0].Items[1].Cost != 20 {
t.Fatalf("月度聚合错误: %+v", months)
}
if result["CVM"].Delta != 80 || result["CVM"].Contribution != 80 {
t.Fatalf("CVM 归因错误: %+v", result["CVM"])
if months[1].Month != "2026-04" || months[1].Total != 0 {
t.Fatalf("空月份未正确补齐: %+v", months[1])
}
if result["COS"].Delta != 20 || result["COS"].Contribution != 20 {
t.Fatalf("COS 归因错误: %+v", result["COS"])
}
// TestBuildBillingAttributions 验证归因变化额与贡献度。 // Logan.Li
func TestBuildBillingAttributions(t *testing.T) {
items := buildBillingAttributions(map[string]float64{"CVM": 100}, map[string]float64{"CVM": 180, "TCR": 20})
if len(items) != 2 || items[0].Name != "CVM" || items[0].Delta != 80 || items[0].Contribution != 80 {
t.Fatalf("费用归因错误: %+v", items)
}
}