From 47c21de613cfb6e9b936735103678c2e53140324 Mon Sep 17 00:00:00 2001 From: BaoXuebin Date: Wed, 24 Nov 2021 23:45:45 +0800 Subject: [PATCH] add stats api impl --- script/bql.go | 7 +-- server.go | 2 + service/stats.go | 110 ++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 110 insertions(+), 9 deletions(-) diff --git a/script/bql.go b/script/bql.go index ce069ae..e2a3653 100644 --- a/script/bql.go +++ b/script/bql.go @@ -16,7 +16,8 @@ type QueryParams struct { Month int `bql:"month ="` Tag string `bql:"in tags"` Account string `bql:"account ="` - AccountType string `bql:"account ~"` + AccountLike string `bql:"account ~"` + GroupBy string `bql:"group by"` OrderBy string `bql:"order by"` Limit int `bql:"limit"` Path string @@ -44,7 +45,7 @@ func GetQueryParams(c *gin.Context) QueryParams { hasWhere = true } if c.Query("type") != "" { - queryParams.AccountType = c.Query("type") + queryParams.AccountLike = c.Query("type") hasWhere = true } if c.Query("account") != "" { @@ -138,7 +139,7 @@ func bqlRawQuery(ledgerConfig *Config, selectBql string, queryParamsPtr *QueryPa case reflect.String: val := valueField.String() if val != "" { - if typeField.Name == "OrderBy" { + if typeField.Name == "OrderBy" || typeField.Name == "GroupBy" { // 去除上一个条件后缀的 AND 关键字 bql = strings.Trim(bql, " AND") bql = fmt.Sprintf("%s %s %s", bql, typeField.Tag.Get("bql"), val) diff --git a/server.go b/server.go index 34a6d03..5791194 100644 --- a/server.go +++ b/server.go @@ -54,6 +54,8 @@ func RegisterRouter(router *gin.Engine) { authorized.GET("/account/type", service.QueryAccountType) authorized.GET("/stats/months", service.MonthsList) authorized.GET("/stats/total", service.StatsTotal) + authorized.GET("/stats/account/percent", service.StatsAccountPercent) + authorized.GET("/stats/account/trend", service.StatsAccountTrend) authorized.GET("/transaction", service.QueryTransactions) authorized.GET("/transaction/payee", service.QueryTransactionPayees) authorized.GET("/transaction/template", service.QueryTransactionTemplates) diff --git a/service/stats.go b/service/stats.go index 36367a2..0454c13 100644 --- a/service/stats.go +++ b/service/stats.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/beancount-gs/script" "github.com/gin-gonic/gin" + "strconv" "strings" ) @@ -27,16 +28,16 @@ func MonthsList(c *gin.Context) { OK(c, months) } -type statsAccountTypeTotal struct { - AccountType string - Amount string +type StatsResult struct { + Key string + Value string } func StatsTotal(c *gin.Context) { ledgerConfig := script.GetLedgerConfigFromContext(c) queryParams := script.GetQueryParams(c) selectBql := fmt.Sprintf("SELECT '\\', root(account, 1), '\\', sum(convert(value(position), '%s')), '\\'", ledgerConfig.OperatingCurrency) - accountTypeTotalList := make([]statsAccountTypeTotal, 0) + accountTypeTotalList := make([]StatsResult, 0) err := script.BQLQueryListByCustomSelect(ledgerConfig, selectBql, &queryParams, &accountTypeTotalList) if err != nil { InternalError(c, err.Error()) @@ -45,11 +46,108 @@ func StatsTotal(c *gin.Context) { result := make(map[string]string, 0) for _, total := range accountTypeTotalList { - fields := strings.Fields(total.Amount) + fields := strings.Fields(total.Value) if len(fields) > 1 { - result[total.AccountType] = fields[0] + result[total.Key] = fields[0] } } OK(c, result) } + +type StatsQuery struct { + Prefix string `form:"prefix" binding:"required"` + Year int `form:"year"` + Month int `form:"month"` + Level int `form:"level"` + Type string `form:"type"` +} + +type AccountPercentResult struct { + Account string `json:"account"` + Amount string `json:"amount"` + OperatingCurrency string `json:"operatingCurrency"` +} + +func StatsAccountPercent(c *gin.Context) { + ledgerConfig := script.GetLedgerConfigFromContext(c) + var statsQuery StatsQuery + if err := c.ShouldBindQuery(&statsQuery); err != nil { + BadRequest(c, err.Error()) + return + } + + queryParams := script.QueryParams{ + AccountLike: statsQuery.Prefix, + Year: statsQuery.Year, + Month: statsQuery.Month, + Where: true, + } + var bql string + if statsQuery.Level != 0 { + prefixNodeLen := len(strings.Split(strings.Trim(statsQuery.Prefix, ":"), ":")) + bql = fmt.Sprintf("SELECT '\\', root(account, %d) as subAccount, '\\', sum(convert(value(position), '%s')), '\\'", statsQuery.Level+prefixNodeLen, ledgerConfig.OperatingCurrency) + } else { + bql = fmt.Sprintf("SELECT '\\', account, '\\', sum(convert(value(position), '%s')), '\\'", ledgerConfig.OperatingCurrency) + } + + statsResultList := make([]AccountPercentResult, 0) + err := script.BQLQueryListByCustomSelect(ledgerConfig, bql, &queryParams, &statsResultList) + if err != nil { + InternalError(c, err.Error()) + return + } + + for idx, result := range statsResultList { + fields := strings.Fields(result.Amount) + statsResultList[idx].Amount = fields[0] + statsResultList[idx].OperatingCurrency = fields[1] + } + OK(c, statsResultList) +} + +type AccountTrendResult struct { + Date string `json:"date"` + Amount float64 `json:"amount"` + OperatingCurrency string `json:"operatingCurrency"` +} + +func StatsAccountTrend(c *gin.Context) { + ledgerConfig := script.GetLedgerConfigFromContext(c) + var statsQuery StatsQuery + if err := c.ShouldBindQuery(&statsQuery); err != nil { + BadRequest(c, err.Error()) + return + } + + queryParams := script.QueryParams{ + AccountLike: statsQuery.Prefix, + Year: statsQuery.Year, + Month: statsQuery.Month, + Where: true, + } + var bql string + if statsQuery.Type == "avg" { + bql = fmt.Sprintf("SELECT '\\', date, '\\', sum(convert(value(position), '%s')), '\\'", ledgerConfig.OperatingCurrency) + } else if statsQuery.Type == "sum" { + bql = fmt.Sprintf("SELECT '\\', date, '\\', convert(balance, '%s'), '\\'", ledgerConfig.OperatingCurrency) + } else { + OK(c, new([]string)) + return + } + + statsResultList := make([]StatsResult, 0) + err := script.BQLQueryListByCustomSelect(ledgerConfig, bql, &queryParams, &statsResultList) + if err != nil { + InternalError(c, err.Error()) + return + } + + result := make([]AccountTrendResult, 0) + for _, stats := range statsResultList { + fields := strings.Fields(stats.Value) + amount, _ := strconv.ParseFloat(fields[0], 32) + result = append(result, AccountTrendResult{Date: stats.Key, Amount: amount, OperatingCurrency: fields[1]}) + } + OK(c, result) +}