From f29a14ad51d84224032419e11060d69f85ad9320 Mon Sep 17 00:00:00 2001 From: MaysWind Date: Sun, 17 Jan 2021 23:41:17 +0800 Subject: [PATCH] add transaction statistics api --- cmd/webserver.go | 3 ++ pkg/api/statistics.go | 53 ++++++++++++++++++++++++++++++++++++ pkg/models/statistic.go | 21 ++++++++++++++ pkg/services/transactions.go | 19 +++++++++++++ 4 files changed, 96 insertions(+) create mode 100644 pkg/api/statistics.go create mode 100644 pkg/models/statistic.go diff --git a/cmd/webserver.go b/cmd/webserver.go index 6d3cbe68..a2b31630 100644 --- a/cmd/webserver.go +++ b/cmd/webserver.go @@ -224,6 +224,9 @@ func startWebServer(c *cli.Context) error { apiV1Route.POST("/transaction/tags/move.json", bindApi(api.TransactionTags.TagMoveHandler)) apiV1Route.POST("/transaction/tags/delete.json", bindApi(api.TransactionTags.TagDeleteHandler)) + // Statistics + apiV1Route.GET("/statistics/transaction.json", bindApi(api.Statistics.TransactionStatisticsHandler)) + // Exchange Rates apiV1Route.GET("/exchange_rates/latest.json", bindApi(api.ExchangeRates.LatestExchangeRateHandler)) } diff --git a/pkg/api/statistics.go b/pkg/api/statistics.go new file mode 100644 index 00000000..18849972 --- /dev/null +++ b/pkg/api/statistics.go @@ -0,0 +1,53 @@ +package api + +import ( + "github.com/mayswind/lab/pkg/core" + "github.com/mayswind/lab/pkg/errs" + "github.com/mayswind/lab/pkg/log" + "github.com/mayswind/lab/pkg/models" + "github.com/mayswind/lab/pkg/services" +) + +// StatisticApi represents statistic api +type StatisticApi struct { + transactions *services.TransactionService +} + +// Initialize an statistic api singleton instance +var ( + Statistics = &StatisticApi{ + transactions: services.Transactions, + } +) + +// TransactionStatisticsHandler returns transaction statistics of current user +func (a *StatisticApi) TransactionStatisticsHandler(c *core.Context) (interface{}, *errs.Error) { + var statisticReq models.TransactionStatisticRequest + err := c.ShouldBindQuery(&statisticReq) + + if err != nil { + log.WarnfWithRequestId(c, "[statistics.TransactionOverviewHandler] parse request failed, because %s", err.Error()) + return nil, errs.NewIncompleteOrIncorrectSubmissionError(err) + } + + uid := c.GetCurrentUid() + totalAmounts, err := a.transactions.GetAccountsAndCategoriesTotalIncomeAndExpense(uid, statisticReq.StartTime, statisticReq.EndTime) + + statisticResp := &models.TransactionStatisticResponse{ + StartTime: statisticReq.StartTime, + EndTime: statisticReq.EndTime, + } + + statisticResp.Items = make([]*models.TransactionStatisticResponseItem, len(totalAmounts)) + + for i := 0; i < len(totalAmounts); i++ { + totalAmountItem := totalAmounts[i] + statisticResp.Items[i] = &models.TransactionStatisticResponseItem{ + CategoryId: totalAmountItem.CategoryId, + AccountId: totalAmountItem.AccountId, + TotalAmount: totalAmountItem.Amount, + } + } + + return statisticResp, nil +} diff --git a/pkg/models/statistic.go b/pkg/models/statistic.go new file mode 100644 index 00000000..421117ba --- /dev/null +++ b/pkg/models/statistic.go @@ -0,0 +1,21 @@ +package models + +// TransactionStatisticRequest represents all parameters of transaction statistic request +type TransactionStatisticRequest struct { + StartTime int64 `form:"start_time" binding:"min=0"` + EndTime int64 `form:"end_time" binding:"min=0"` +} + +// TransactionStatisticResponse represents an item of transaction overview +type TransactionStatisticResponse struct { + StartTime int64 `json:"startTime"` + EndTime int64 `json:"endTime"` + Items []*TransactionStatisticResponseItem `json:"items"` +} + +// TransactionStatisticResponseItem represents total amount item for an response +type TransactionStatisticResponseItem struct { + CategoryId int64 `json:"categoryId,string"` + AccountId int64 `json:"accountId,string"` + TotalAmount int64 `json:"amount"` +} diff --git a/pkg/services/transactions.go b/pkg/services/transactions.go index 76445b26..66fac319 100644 --- a/pkg/services/transactions.go +++ b/pkg/services/transactions.go @@ -955,6 +955,25 @@ func (s *TransactionService) GetAccountsTotalIncomeAndExpense(uid int64, startUn return incomeAmounts, expenseAmounts, nil } +// GetAccountsAndCategoriesTotalIncomeAndExpense returns the every accounts and categories total income and expense amount by specific date range +func (s *TransactionService) GetAccountsAndCategoriesTotalIncomeAndExpense(uid int64, startUnixTime int64, endUnixTime int64) ([]*models.Transaction, error) { + if uid <= 0 { + return nil, errs.ErrUserIdInvalid + } + + startTransactionTime := utils.GetMinTransactionTimeFromUnixTime(startUnixTime) + endTransactionTime := utils.GetMaxTransactionTimeFromUnixTime(endUnixTime) + + var transactionTotalAmounts []*models.Transaction + err := s.UserDataDB(uid).Select("uid, category_id, account_id, SUM(amount) as amount").Where("uid=? AND deleted=? AND (type=? OR type=?) AND transaction_time>=? AND transaction_time<=?", uid, false, models.TRANSACTION_DB_TYPE_INCOME, models.TRANSACTION_DB_TYPE_EXPENSE, startTransactionTime, endTransactionTime).GroupBy("category_id, account_id").Find(&transactionTotalAmounts) + + if err != nil { + return nil, err + } + + return transactionTotalAmounts, nil +} + // GetTransactionMapByList returns a transaction map by a list func (s *TransactionService) GetTransactionMapByList(transactions []*models.Transaction) map[int64]*models.Transaction { transactionMap := make(map[int64]*models.Transaction)