feat: currency account add excahnge rate value and currency flag

This commit is contained in:
BaoXuebin 2023-12-07 13:57:43 +08:00
parent 512a6799c5
commit 89f287fe90
7 changed files with 168 additions and 103 deletions

View File

@ -6,12 +6,14 @@ import (
"os" "os"
"sort" "sort"
"strings" "strings"
"time"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
var serverSecret string var serverSecret string
var serverConfig Config var serverConfig Config
var serverCurrencies []LedgerCurrency
var ledgerConfigMap map[string]Config var ledgerConfigMap map[string]Config
var ledgerAccountsMap map[string][]Account var ledgerAccountsMap map[string][]Account
var ledgerAccountTypesMap map[string]map[string]string var ledgerAccountTypesMap map[string]map[string]string
@ -33,7 +35,10 @@ type Config struct {
type Account struct { type Account struct {
Acc string `json:"account"` Acc string `json:"account"`
StartDate string `json:"startDate"` StartDate string `json:"startDate"`
Currency string `json:"currency,omitempty"` Currency string `json:"currency,omitempty"` // 货币
CurrencySymbol string `json:"currencySymbol,omitempty"` // 货币符号
ExRate string `json:"exRate,omitempty"` // 汇率
ExDate string `json:"exDate,omitempty"` // 汇率日期
Positions []AccountPosition `json:"positions,omitempty"` Positions []AccountPosition `json:"positions,omitempty"`
MarketNumber string `json:"marketNumber,omitempty"` MarketNumber string `json:"marketNumber,omitempty"`
MarketCurrency string `json:"marketCurrency,omitempty"` MarketCurrency string `json:"marketCurrency,omitempty"`
@ -57,6 +62,10 @@ type LedgerCurrency struct {
Name string `json:"name"` Name string `json:"name"`
Currency string `json:"currency"` Currency string `json:"currency"`
Symbol string `json:"symbol"` Symbol string `json:"symbol"`
IsCurrent bool `json:"isCurrent,omitempty"` // 是否是货币(非货币的为投资单位)
Current bool `json:"current,omitempty"`
ExRate string `json:"exRate,omitempty"`
Date string `json:"date,omitempty"`
} }
func GetServerConfig() Config { func GetServerConfig() Config {
@ -194,7 +203,7 @@ func GetAccountType(ledgerId string, acc string) AccountType {
// 默认取最后一个节点 // 默认取最后一个节点
Name: accNodes[len(accNodes)-1], Name: accNodes[len(accNodes)-1],
} }
var matchKey string = "" var matchKey = ""
for key, name := range accountTypes { for key, name := range accountTypes {
if strings.Contains(acc, key) && len(matchKey) < len(key) { if strings.Contains(acc, key) && len(matchKey) < len(key) {
matchKey = key matchKey = key
@ -386,14 +395,32 @@ func EqualServerSecret(secret string) bool {
return serverSecret == secret return serverSecret == secret
} }
func LoadServerCurrencyMap() {
if serverCurrencies == nil {
serverCurrencies = make([]LedgerCurrency, 0)
}
serverCurrencies = append(serverCurrencies, LedgerCurrency{Name: "人民币", Currency: "CNY", Symbol: "¥"})
serverCurrencies = append(serverCurrencies, LedgerCurrency{Name: "美元", Currency: "USD", Symbol: "$"})
serverCurrencies = append(serverCurrencies, LedgerCurrency{Name: "欧元", Currency: "EUR", Symbol: "€"})
serverCurrencies = append(serverCurrencies, LedgerCurrency{Name: "日元", Currency: "JPY", Symbol: "¥"})
serverCurrencies = append(serverCurrencies, LedgerCurrency{Name: "加拿大元", Currency: "CAD", Symbol: "$"})
serverCurrencies = append(serverCurrencies, LedgerCurrency{Name: "俄罗斯卢布", Currency: "RUB", Symbol: "₽"})
}
func LoadLedgerCurrencyMap(config Config) error { func LoadLedgerCurrencyMap(config Config) error {
LoadServerCurrencyMap()
path := GetLedgerCurrenciesFilePath(config.DataPath) path := GetLedgerCurrenciesFilePath(config.DataPath)
if !FileIfExist(path) { if !FileIfExist(path) {
err := CreateFile(path) err := CreateFile(path)
if err != nil { if err != nil {
return err return err
} }
err = WriteFile(path, "[{\"name\":\"人民币\",\"symbol\":\"¥\",\"currency\":\"CNY\"},{\"name\":\"美元\",\"symbol\":\"$\",\"currency\":\"USD\"},{\"name\":\"欧元\",\"symbol\":\"€\",\"currency\":\"EUR\"},{\"name\":\"英镑\",\"symbol\":\"£\",\"currency\":\"GBP\"},{\"name\":\"日元\",\"symbol\":\"¥\",\"currency\":\"JPY\"},{\"name\":\"加拿大元\",\"symbol\":\"$\",\"currency\":\"CAD\"},{\"name\":\"澳大利亚元\",\"symbol\":\"$\",\"currency\":\"AUD\"},{\"name\":\"瑞士法郎\",\"symbol\":\"CHF\",\"currency\":\"CHF\"},{\"name\":\"俄罗斯卢布\",\"symbol\":\"₽\",\"currency\":\"RUB\"}]")
bytes, err := json.Marshal(serverCurrencies)
if err != nil {
return err
}
err = WriteFile(path, string(bytes))
if err != nil { if err != nil {
return err return err
} }
@ -414,6 +441,8 @@ func LoadLedgerCurrencyMap(config Config) error {
} }
ledgerCurrencyMap[config.Id] = currencies ledgerCurrencyMap[config.Id] = currencies
LogSystemInfo(fmt.Sprintf("Success load [%s] account type cache", config.Mail)) LogSystemInfo(fmt.Sprintf("Success load [%s] account type cache", config.Mail))
// 刷新汇率
RefreshLedgerCurrency(&config)
return nil return nil
} }
@ -421,28 +450,97 @@ func GetLedgerCurrency(ledgerId string) []LedgerCurrency {
return ledgerCurrencyMap[ledgerId] return ledgerCurrencyMap[ledgerId]
} }
func GetCommoditySymbol(commodity string) string { type CommodityPrice struct {
switch commodity { Date string `json:"date"`
case "CNY": Commodity string `json:"commodity"`
return "¥" Currency string `json:"operatingCurrency"`
case "USD": Value string `json:"value"`
return "$" }
case "EUR":
return "€" func RefreshLedgerCurrency(ledgerConfig *Config) []LedgerCurrency {
case "JPY": // 查询货币获取当前汇率
return "¥" output := BeanReportAllPrices(ledgerConfig)
case "GBP": statsPricesResultList := make([]CommodityPrice, 0)
return "£" lines := strings.Split(output, "\n")
case "AUD": // foreach lines
return "$" for _, line := range lines {
case "CAD": if strings.Trim(line, " ") == "" {
return "$" continue
case "INR": }
return "₹" // split line by " "
case "RUB": words := strings.Fields(line)
return "₽" statsPricesResultList = append(statsPricesResultList, CommodityPrice{
case "BRL": Date: words[0],
return "R$" Commodity: words[2],
Value: words[3],
Currency: words[4],
})
}
// statsPricesResultList 转为 map
existCurrencyMap := make(map[string]CommodityPrice)
for _, statsPricesResult := range statsPricesResultList {
existCurrencyMap[statsPricesResult.Commodity] = statsPricesResult
}
result := make([]LedgerCurrency, 0)
currencies := GetLedgerCurrency(ledgerConfig.Id)
for _, c := range currencies {
current := c.Currency == ledgerConfig.OperatingCurrency
var exRate string
var date string
if current {
exRate = "1"
date = time.Now().Format("2006-01-02")
} else {
value, exists := existCurrencyMap[c.Currency]
if exists {
exRate = value.Value
date = value.Date
}
}
result = append(result, LedgerCurrency{
Name: c.Name,
Currency: c.Currency,
Symbol: c.Symbol,
Current: current,
ExRate: exRate,
Date: date,
})
}
// 刷新账本货币缓存
ledgerCurrencyMap[ledgerConfig.Id] = result
return result
}
func GetLedgerCurrencyMap(ledgerId string) map[string]LedgerCurrency {
currencyMap := make(map[string]LedgerCurrency)
currencies := GetLedgerCurrency(ledgerId)
if currencies == nil {
return currencyMap
}
for _, currency := range currencies {
currencyMap[currency.Currency] = currency
}
return currencyMap
}
func GetCommoditySymbol(ledgerId string, commodity string) string {
currencyMap := GetLedgerCurrencyMap(ledgerId)
if currencyMap == nil {
return commodity
}
if _, ok := currencyMap[commodity]; !ok {
return commodity
}
return currencyMap[commodity].Symbol
}
func GetServerCommoditySymbol(commodity string) string {
for _, currency := range serverCurrencies {
if currency.Currency == commodity {
return currency.Symbol
}
} }
return commodity return commodity
} }

View File

@ -14,9 +14,21 @@ import (
func QueryValidAccount(c *gin.Context) { func QueryValidAccount(c *gin.Context) {
ledgerConfig := script.GetLedgerConfigFromContext(c) ledgerConfig := script.GetLedgerConfigFromContext(c)
allAccounts := script.GetLedgerAccounts(ledgerConfig.Id) allAccounts := script.GetLedgerAccounts(ledgerConfig.Id)
currencyMap := script.GetLedgerCurrencyMap(ledgerConfig.Id)
result := make([]script.Account, 0) result := make([]script.Account, 0)
for _, account := range allAccounts { for _, account := range allAccounts {
if account.EndDate == "" { if account.EndDate == "" {
// 货币实时汇率(忽略账本主货币)
if account.Currency != ledgerConfig.OperatingCurrency && account.Currency != "" {
// 从 map 中获取对应货币的实时汇率和符号
currency, ok := currencyMap[account.Currency]
if ok {
account.CurrencySymbol = currency.Symbol
account.ExRate = currency.ExRate
account.ExDate = currency.Date
account.IsAnotherCurrency = true
}
}
result = append(result, account) result = append(result, account)
} }
} }
@ -45,6 +57,7 @@ func QueryAllAccount(c *gin.Context) {
accountPositionMap[ap.Account] = ap accountPositionMap[ap.Account] = ap
} }
currencyMap := script.GetLedgerCurrencyMap(ledgerConfig.Id)
accounts := script.GetLedgerAccounts(ledgerConfig.Id) accounts := script.GetLedgerAccounts(ledgerConfig.Id)
result := make([]script.Account, 0, len(accounts)) result := make([]script.Account, 0, len(accounts))
for i := 0; i < len(accounts); i++ { for i := 0; i < len(accounts); i++ {
@ -53,6 +66,17 @@ func QueryAllAccount(c *gin.Context) {
if account.EndDate != "" { if account.EndDate != "" {
continue continue
} }
// 货币实时汇率(忽略账本主货币)
if account.Currency != ledgerConfig.OperatingCurrency && account.Currency != "" {
// 从 map 中获取对应货币的实时汇率和符号
currency, ok := currencyMap[account.Currency]
if ok {
account.CurrencySymbol = currency.Symbol
account.ExRate = currency.ExRate
account.ExDate = currency.Date
account.IsAnotherCurrency = true
}
}
key := account.Acc key := account.Acc
typ := script.GetAccountType(ledgerConfig.Id, key) typ := script.GetAccountType(ledgerConfig.Id, key)
account.Type = &typ account.Type = &typ
@ -61,18 +85,18 @@ func QueryAllAccount(c *gin.Context) {
fields := strings.Fields(marketPosition) fields := strings.Fields(marketPosition)
account.MarketNumber = fields[0] account.MarketNumber = fields[0]
account.MarketCurrency = fields[1] account.MarketCurrency = fields[1]
account.MarketCurrencySymbol = script.GetCommoditySymbol(fields[1]) account.MarketCurrencySymbol = script.GetCommoditySymbol(ledgerConfig.Id, fields[1])
} }
position := strings.Trim(accountPositionMap[key].Position, " ") position := strings.Trim(accountPositionMap[key].Position, " ")
if position != "" { if position != "" {
account.Positions = parseAccountPositions(position) account.Positions = parseAccountPositions(ledgerConfig.Id, position)
} }
result = append(result, account) result = append(result, account)
} }
OK(c, result) OK(c, result)
} }
func parseAccountPositions(input string) []script.AccountPosition { func parseAccountPositions(ledgerId string, input string) []script.AccountPosition {
// 使用正则表达式提取数字、货币代码和金额 // 使用正则表达式提取数字、货币代码和金额
re := regexp.MustCompile(`(-?\d+\.\d+) (\w+)`) re := regexp.MustCompile(`(-?\d+\.\d+) (\w+)`)
matches := re.FindAllStringSubmatch(input, -1) matches := re.FindAllStringSubmatch(input, -1)
@ -85,7 +109,7 @@ func parseAccountPositions(input string) []script.AccountPosition {
currency := match[2] currency := match[2]
// 获取货币符号 // 获取货币符号
symbol := script.GetCommoditySymbol(currency) symbol := script.GetCommoditySymbol(ledgerId, currency)
// 创建 AccountPosition // 创建 AccountPosition
position := script.AccountPosition{ position := script.AccountPosition{
@ -294,7 +318,7 @@ func BalanceAccount(c *gin.Context) {
result["date"] = accountForm.Date result["date"] = accountForm.Date
result["marketNumber"] = accountForm.Number result["marketNumber"] = accountForm.Number
result["marketCurrency"] = ledgerConfig.OperatingCurrency result["marketCurrency"] = ledgerConfig.OperatingCurrency
result["marketCurrencySymbol"] = script.GetCommoditySymbol(ledgerConfig.OperatingCurrency) result["marketCurrencySymbol"] = script.GetCommoditySymbol(ledgerConfig.Id, ledgerConfig.OperatingCurrency)
OK(c, result) OK(c, result)
} }

View File

@ -4,8 +4,6 @@ import (
"fmt" "fmt"
"github.com/beancount-gs/script" "github.com/beancount-gs/script"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"strings"
"time"
) )
type SyncCommodityPriceForm struct { type SyncCommodityPriceForm struct {
@ -30,70 +28,15 @@ func SyncCommodityPrice(c *gin.Context) {
InternalError(c, err.Error()) InternalError(c, err.Error())
return return
} }
OK(c, syncCommodityPriceForm)
}
type CommodityCurrency struct { // 刷新货币最新汇率值
Name string `json:"name"` script.RefreshLedgerCurrency(ledgerConfig)
Currency string `json:"currency"` OK(c, syncCommodityPriceForm)
Symbol string `json:"symbol"`
Current bool `json:"current"`
ExRate string `json:"exRate"`
Date string `json:"date"`
} }
func QueryAllCurrencies(c *gin.Context) { func QueryAllCurrencies(c *gin.Context) {
ledgerConfig := script.GetLedgerConfigFromContext(c) ledgerConfig := script.GetLedgerConfigFromContext(c)
// 查询货币获取当前汇率 // 查询货币获取当前汇率
output := script.BeanReportAllPrices(ledgerConfig) currency := script.RefreshLedgerCurrency(ledgerConfig)
statsPricesResultList := make([]StatsPricesResult, 0) OK(c, currency)
lines := strings.Split(output, "\n")
// foreach lines
for _, line := range lines {
if strings.Trim(line, " ") == "" {
continue
}
// split line by " "
words := strings.Fields(line)
statsPricesResultList = append(statsPricesResultList, StatsPricesResult{
Date: words[0],
Commodity: words[2],
Value: words[3],
Currency: words[4],
})
}
// statsPricesResultList 转为 map
existCurrencyMap := make(map[string]StatsPricesResult)
for _, statsPricesResult := range statsPricesResultList {
existCurrencyMap[statsPricesResult.Commodity] = statsPricesResult
}
result := make([]CommodityCurrency, 0)
currencies := script.GetLedgerCurrency(ledgerConfig.Id)
for _, c := range currencies {
current := c.Currency == ledgerConfig.OperatingCurrency
var exRate string
var date string
if current {
exRate = "1"
date = time.Now().Format("2006-01-02")
} else {
value, exists := existCurrencyMap[c.Currency]
if exists {
exRate = value.Value
date = value.Date
}
}
result = append(result, CommodityCurrency{
Name: c.Name,
Currency: c.Currency,
Symbol: c.Symbol,
Current: current,
ExRate: exRate,
Date: date,
})
}
OK(c, result)
} }

View File

@ -23,7 +23,7 @@ func ImportAliPayCSV(c *gin.Context) {
result := make([]Transaction, 0) result := make([]Transaction, 0)
currency := "CNY" currency := "CNY"
currencySymbol := script.GetCommoditySymbol(currency) currencySymbol := script.GetCommoditySymbol(ledgerConfig.Id, currency)
for { for {
lines, err := reader.Read() lines, err := reader.Read()
@ -124,7 +124,7 @@ func ImportWxPayCSV(c *gin.Context) {
result := make([]Transaction, 0) result := make([]Transaction, 0)
currency := "CNY" currency := "CNY"
currencySymbol := script.GetCommoditySymbol(currency) currencySymbol := script.GetCommoditySymbol(ledgerConfig.Id, currency)
for { for {
lines, err := reader.Read() lines, err := reader.Read()
@ -173,7 +173,7 @@ func ImportICBCCSV(c *gin.Context) {
result := make([]Transaction, 0) result := make([]Transaction, 0)
currency := "CNY" currency := "CNY"
currencySymbol := script.GetCommoditySymbol(currency) currencySymbol := script.GetCommoditySymbol(ledgerConfig.Id, currency)
id := 0 id := 0
for { for {
@ -226,7 +226,7 @@ func ImportABCCSV(c *gin.Context) {
result := make([]Transaction, 0) result := make([]Transaction, 0)
currency := "CNY" currency := "CNY"
currencySymbol := script.GetCommoditySymbol(currency) currencySymbol := script.GetCommoditySymbol(ledgerConfig.Id, currency)
id := 0 id := 0
for { for {

View File

@ -160,7 +160,7 @@ func OpenOrCreateLedger(c *gin.Context) {
resultMap["ledgerId"] = ledgerId resultMap["ledgerId"] = ledgerId
resultMap["title"] = userLedger.Title resultMap["title"] = userLedger.Title
resultMap["currency"] = userLedger.OperatingCurrency resultMap["currency"] = userLedger.OperatingCurrency
resultMap["currencySymbol"] = script.GetCommoditySymbol(userLedger.OperatingCurrency) resultMap["currencySymbol"] = script.GetServerCommoditySymbol(userLedger.OperatingCurrency)
resultMap["createDate"] = userLedger.CreateDate resultMap["createDate"] = userLedger.CreateDate
OK(c, resultMap) OK(c, resultMap)
return return
@ -176,7 +176,7 @@ func OpenOrCreateLedger(c *gin.Context) {
resultMap["ledgerId"] = ledgerId resultMap["ledgerId"] = ledgerId
resultMap["title"] = userLedger.Title resultMap["title"] = userLedger.Title
resultMap["currency"] = userLedger.OperatingCurrency resultMap["currency"] = userLedger.OperatingCurrency
resultMap["currencySymbol"] = script.GetCommoditySymbol(userLedger.OperatingCurrency) resultMap["currencySymbol"] = script.GetCommoditySymbol(ledgerId, userLedger.OperatingCurrency)
resultMap["createDate"] = userLedger.CreateDate resultMap["createDate"] = userLedger.CreateDate
OK(c, resultMap) OK(c, resultMap)
} }

View File

@ -386,7 +386,7 @@ func StatsMonthCalendar(c *gin.Context) {
Account: queryRes.Account, Account: queryRes.Account,
Amount: json.Number(fields[0]), Amount: json.Number(fields[0]),
Currency: fields[1], Currency: fields[1],
CurrencySymbol: script.GetCommoditySymbol(fields[1]), CurrencySymbol: script.GetCommoditySymbol(ledgerConfig.Id, fields[1]),
}) })
} }
} }

View File

@ -46,7 +46,7 @@ func QueryTransactions(c *gin.Context) {
} }
// 格式化金额 // 格式化金额
for i := 0; i < len(transactions); i++ { for i := 0; i < len(transactions); i++ {
symbol := script.GetCommoditySymbol(transactions[i].Currency) symbol := script.GetCommoditySymbol(ledgerConfig.Id, transactions[i].Currency)
transactions[i].CurrencySymbol = symbol transactions[i].CurrencySymbol = symbol
transactions[i].CostCurrencySymbol = symbol transactions[i].CostCurrencySymbol = symbol
if transactions[i].Price != "" { if transactions[i].Price != "" {