Money工具包:实现分↔角转换和格式化函数

核心函数:
- ToCent(jiao) -> cent:将角转换为分(存储)
- ToJiao(cent) -> jiao:将分转换为角(展示)
- Format(cent) -> string:格式化分为角字符串
- FormatWithSymbol(cent) -> string:格式化为带¥符号的字符串

兼容性:
- 保留 Min/Max(float64) 用于兼容旧代码
- 新增 MinCent/MaxCent(int64) 用于分计算

测试覆盖:
- 双向转换精度测试
- 边界值测试
- 格式化输出测试

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
yml
2026-06-09 13:34:09 +08:00
co-authored by Claude Opus 4.8
parent b80719d538
commit e2780ffb86
2 changed files with 202 additions and 11 deletions
+42 -11
View File
@@ -11,7 +11,35 @@ func Round(value float64) float64 {
return math.Round(value*10) / 10 return math.Round(value*10) / 10
} }
// Min 返回两个金额中较小的值(角精度) // ToCent 将角转换为分(整数),用于存储
// 12.3角 -> 123分
// 12.34角 -> 123分(自动舍入到角)
func ToCent(jiao float64) int64 {
return int64(math.Round(jiao * 10))
}
// ToJiao 将分转换为角(0.1元精度),用于API响应
// 123分 -> 12.3角
// 1234分 -> 123.4角
func ToJiao(cent int64) float64 {
return float64(cent) / 10.0
}
// Format 格式化分为字符串(角精度,保留1位小数)
// 123分 -> "12.3"
// 1230分 -> "123.0"
func Format(cent int64) string {
jiao := ToJiao(cent)
return fmt.Sprintf("%.1f", jiao)
}
// FormatWithSymbol 格式化分为带符号的字符串
// 123分 -> "¥12.3"
func FormatWithSymbol(cent int64) string {
return "¥" + Format(cent)
}
// Min 返回两个金额中较小的值(角精度,兼容旧代码)
func Min(a, b float64) float64 { func Min(a, b float64) float64 {
if a < b { if a < b {
return Round(a) return Round(a)
@@ -19,7 +47,7 @@ func Min(a, b float64) float64 {
return Round(b) return Round(b)
} }
// Max 返回两个金额中较大的值(角精度) // Max 返回两个金额中较大的值(角精度,兼容旧代码
func Max(a, b float64) float64 { func Max(a, b float64) float64 {
if a > b { if a > b {
return Round(a) return Round(a)
@@ -27,15 +55,18 @@ func Max(a, b float64) float64 {
return Round(b) return Round(b)
} }
// Format 格式化金额为字符串(保留1位小数) // MinCent 返回两个分值中较小的值
// 例如:12.3 -> "12.3", 12.0 -> "12.0" func MinCent(a, b int64) int64 {
func Format(value float64) string { if a < b {
rounded := Round(value) return a
return fmt.Sprintf("%.1f", rounded) }
return b
} }
// FormatWithSymbol 格式化金额并添加货币符号 // MaxCent 返回两个分值中较大的值
// 例如:12.3 -> "¥12.3" func MaxCent(a, b int64) int64 {
func FormatWithSymbol(value float64) string { if a > b {
return "¥" + Format(value) return a
}
return b
} }
+160
View File
@@ -0,0 +1,160 @@
package money
import "testing"
func TestToCent(t *testing.T) {
tests := []struct {
name string
jiao float64
want int64
}{
{"12.3角转123分", 12.3, 123},
{"12.34角舍入到123分", 12.34, 123},
{"12.36角舍入到124分", 12.36, 124},
{"0.1角转1分", 0.1, 1},
{"0.05角舍入到1分", 0.05, 1},
{"0.04角舍入到0分", 0.04, 0},
{"100角转1000分", 100.0, 1000},
{"零值", 0.0, 0},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := ToCent(tt.jiao)
if got != tt.want {
t.Errorf("ToCent(%v) = %v, want %v", tt.jiao, got, tt.want)
}
})
}
}
func TestToJiao(t *testing.T) {
tests := []struct {
name string
cent int64
want float64
}{
{"123分转12.3角", 123, 12.3},
{"1234分转123.4角", 1234, 123.4},
{"1分转0.1角", 1, 0.1},
{"10分转1.0角", 10, 1.0},
{"1000分转100.0角", 1000, 100.0},
{"零值", 0, 0.0},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := ToJiao(tt.cent)
if got != tt.want {
t.Errorf("ToJiao(%v) = %v, want %v", tt.cent, got, tt.want)
}
})
}
}
func TestFormat(t *testing.T) {
tests := []struct {
name string
cent int64
want string
}{
{"123分格式化", 123, "12.3"},
{"1234分格式化", 1234, "123.4"},
{"10分格式化", 10, "1.0"},
{"1分格式化", 1, "0.1"},
{"1000分格式化", 1000, "100.0"},
{"零值格式化", 0, "0.0"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := Format(tt.cent)
if got != tt.want {
t.Errorf("Format(%v) = %v, want %v", tt.cent, got, tt.want)
}
})
}
}
func TestFormatWithSymbol(t *testing.T) {
tests := []struct {
name string
cent int64
want string
}{
{"123分带符号", 123, "¥12.3"},
{"1000分带符号", 1000, "¥100.0"},
{"零值带符号", 0, "¥0.0"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := FormatWithSymbol(tt.cent)
if got != tt.want {
t.Errorf("FormatWithSymbol(%v) = %v, want %v", tt.cent, got, tt.want)
}
})
}
}
func TestRound(t *testing.T) {
tests := []struct {
name string
value float64
want float64
}{
{"12.34舍入到12.3", 12.34, 12.3},
{"12.36舍入到12.4", 12.36, 12.4},
{"12.35舍入到12.4", 12.35, 12.4},
{"0.05舍入到0.1", 0.05, 0.1},
{"0.04舍入到0.0", 0.04, 0.0},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := Round(tt.value)
if got != tt.want {
t.Errorf("Round(%v) = %v, want %v", tt.value, got, tt.want)
}
})
}
}
func TestMinMax(t *testing.T) {
t.Run("Min", func(t *testing.T) {
if got := Min(100, 200); got != 100 {
t.Errorf("Min(100, 200) = %v, want 100", got)
}
if got := Min(200, 100); got != 100 {
t.Errorf("Min(200, 100) = %v, want 100", got)
}
})
t.Run("Max", func(t *testing.T) {
if got := Max(100, 200); got != 200 {
t.Errorf("Max(100, 200) = %v, want 200", got)
}
if got := Max(200, 100); got != 200 {
t.Errorf("Max(200, 100) = %v, want 200", got)
}
})
}
// 测试双向转换的精度
func TestRoundTrip(t *testing.T) {
tests := []struct {
name string
originalJiao float64
expectJiao float64 // 因为角精度,可能会有舍入
}{
{"12.3角往返", 12.3, 12.3},
{"12.34角往返(舍入)", 12.34, 12.3},
{"100.0角往返", 100.0, 100.0},
{"0.1角往返", 0.1, 0.1},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cent := ToCent(tt.originalJiao)
gotJiao := ToJiao(cent)
if gotJiao != tt.expectJiao {
t.Errorf("往返转换:%v角 -> %v分 -> %v角, 期望 %v角",
tt.originalJiao, cent, gotJiao, tt.expectJiao)
}
})
}
}