init: initial commit
Checks / verify (push) Canceled after 41s

This commit is contained in:
2026-10-06 11:24:02 +08:00
parent 456e87d4f2
commit ba6dfffab7
63 changed files with 7223 additions and 0 deletions
@@ -0,0 +1,15 @@
package domain
import "time"
type Account struct {
ID string `db:"id" json:"id"`
UserID string `db:"user_id" json:"user_id"`
Mode string `db:"mode" json:"mode"`
Name string `db:"name" json:"name"`
Currency string `db:"currency" json:"currency"`
LedgerStatus string `db:"ledger_status" json:"ledger_status"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
}
func (Account) TableName() string { return "veyra_account" }
@@ -0,0 +1,19 @@
package domain
import "time"
// CashEntry amounts are integer CNY cents; no floating-point accounting.
type CashEntry struct {
ID string `db:"id"`
AccountID string `db:"account_id"`
EntryType string `db:"entry_type"`
AmountMinor int64 `db:"amount_minor"`
CreatedAt time.Time `db:"created_at"`
}
func (CashEntry) TableName() string { return "veyra_cash_entry" }
type AccountSummary struct {
Account
CashBalance *string `json:"cash_balance"`
}
@@ -0,0 +1,35 @@
package domain
import (
"fmt"
"regexp"
"strconv"
"strings"
"veyra/services/api/internal/platform/apperror"
)
var moneyPattern = regexp.MustCompile(`^(0|[1-9][0-9]{0,9})(\.[0-9]{1,2})?$`)
// ParseMoney converts decimal text into exact cents, rejecting rounding and exponents.
func ParseMoney(value string) (int64, error) {
if !moneyPattern.MatchString(value) {
return 0, apperror.ErrInvalidInput
}
whole, fraction, _ := strings.Cut(value, ".")
units, err := strconv.ParseInt(whole, 10, 64)
if err != nil {
return 0, apperror.ErrInvalidInput
}
fraction += strings.Repeat("0", 2-len(fraction))
cents, err := strconv.ParseInt(fraction, 10, 64)
if err != nil {
return 0, apperror.ErrInvalidInput
}
amount := units*100 + cents
if amount > 100000000000 {
return 0, apperror.ErrInvalidInput
}
return amount, nil
}
func FormatMoney(minor int64) string { return fmt.Sprintf("%d.%02d", minor/100, minor%100) }
@@ -0,0 +1,21 @@
package domain
import "testing"
func TestExactMoney(t *testing.T) {
for _, tc := range []struct {
input string
minor int64
formatted string
}{{"0", 0, "0.00"}, {"0.01", 1, "0.01"}, {"100000.10", 10000010, "100000.10"}, {"3.1", 310, "3.10"}, {"1000000000.00", 100000000000, "1000000000.00"}} {
amount, err := ParseMoney(tc.input)
if err != nil || amount != tc.minor || FormatMoney(amount) != tc.formatted {
t.Fatalf("%s: amount=%d err=%v", tc.input, amount, err)
}
}
for _, input := range []string{"-1", "1.001", "1e3", "NaN", " 1", "01", "1.", "1000000000.01", "999999999999999999999", ""} {
if _, err := ParseMoney(input); err == nil {
t.Fatalf("accepted invalid amount %q", input)
}
}
}