diff --git a/cmd/webserver.go b/cmd/webserver.go index 846008d5..515409ab 100644 --- a/cmd/webserver.go +++ b/cmd/webserver.go @@ -204,6 +204,7 @@ func startWebServer(c *cli.Context) error { apiV1Route.POST("/accounts/delete.json", bindApi(api.Accounts.AccountDeleteHandler)) // Transactions + apiV1Route.GET("/transactions/count.json", bindApi(api.Transactions.TransactionCountHandler)) apiV1Route.GET("/transactions/list.json", bindApi(api.Transactions.TransactionListHandler)) apiV1Route.GET("/transactions/list/by_month.json", bindApi(api.Transactions.TransactionListHandler)) apiV1Route.GET("/transactions/get.json", bindApi(api.Transactions.TransactionGetHandler)) diff --git a/pkg/api/transactions.go b/pkg/api/transactions.go index ea52a0f2..2efc26df 100644 --- a/pkg/api/transactions.go +++ b/pkg/api/transactions.go @@ -31,6 +31,46 @@ var ( } ) +// TransactionCountHandler returns transaction total count of current user +func (a *TransactionsApi) TransactionCountHandler(c *core.Context) (interface{}, *errs.Error) { + var transactionCountReq models.TransactionCountRequest + err := c.ShouldBindQuery(&transactionCountReq) + + if err != nil { + log.WarnfWithRequestId(c, "[transactions.TransactionCountHandler] parse request failed, because %s", err.Error()) + return nil, errs.NewIncompleteOrIncorrectSubmissionError(err) + } + + uid := c.GetCurrentUid() + + var allCategoryIds []int64 + + if transactionCountReq.CategoryId > 0 { + allSubCategories, err := a.transactionCategories.GetAllCategoriesByUid(uid, 0, transactionCountReq.CategoryId) + + if err != nil { + log.WarnfWithRequestId(c, "[transactions.TransactionCountHandler] get transaction category error, because %s", err.Error()) + return nil, errs.ErrOperationFailed + } + + if len(allSubCategories) > 0 { + for i := 0; i < len(allSubCategories); i++ { + allCategoryIds = append(allCategoryIds, allSubCategories[i].CategoryId) + } + } else { + allCategoryIds = append(allCategoryIds, transactionCountReq.CategoryId) + } + } + + totalCount, err := a.transactions.GetTransactionCount(uid, transactionCountReq.MaxTime, transactionCountReq.MinTime, transactionCountReq.Type, allCategoryIds, transactionCountReq.AccountId, transactionCountReq.Keyword) + + countResp := &models.TransactionCountResponse{ + Count: totalCount, + } + + return countResp, nil +} + // TransactionListHandler returns transaction list of current user func (a *TransactionsApi) TransactionListHandler(c *core.Context) (interface{}, *errs.Error) { var transactionListReq models.TransactionListByMaxTimeRequest diff --git a/pkg/models/transaction.go b/pkg/models/transaction.go index 096116a0..360dcd7f 100644 --- a/pkg/models/transaction.go +++ b/pkg/models/transaction.go @@ -80,6 +80,16 @@ type TransactionModifyRequest struct { Comment string `json:"comment" binding:"max=255"` } +// TransactionCountRequest represents transaction count request +type TransactionCountRequest struct { + Type TransactionDbType `form:"type" binding:"min=0,max=4"` + CategoryId int64 `form:"category_id" binding:"min=0"` + AccountId int64 `form:"account_id" binding:"min=0"` + Keyword string `form:"keyword"` + MaxTime int64 `form:"max_time" binding:"min=0"` + MinTime int64 `form:"min_time" binding:"min=0"` +} + // TransactionListByMaxTimeRequest represents all parameters of transaction listing by max time request type TransactionListByMaxTimeRequest struct { Type TransactionDbType `form:"type" binding:"min=0,max=4"` @@ -143,6 +153,11 @@ type TransactionInfoResponse struct { Editable bool `json:"editable"` } +// TransactionCountResponse represents transaction count response +type TransactionCountResponse struct { + Count int64 `json:"count"` +} + // TransactionInfoPageWrapperResponse represents a response of transaction which contains items and next id type TransactionInfoPageWrapperResponse struct { Items TransactionInfoResponseSlice `json:"items"` diff --git a/pkg/services/transactions.go b/pkg/services/transactions.go index 30c712fd..f9eba1c0 100644 --- a/pkg/services/transactions.go +++ b/pkg/services/transactions.go @@ -75,68 +75,7 @@ func (s *TransactionService) GetTransactionsByMaxTime(uid int64, maxTransactionT var transactions []*models.Transaction var err error - condition := "uid=? AND deleted=?" - conditionParams := make([]interface{}, 0, 16) - conditionParams = append(conditionParams, uid) - conditionParams = append(conditionParams, false) - - if models.TRANSACTION_DB_TYPE_MODIFY_BALANCE <= transactionType && transactionType <= models.TRANSACTION_DB_TYPE_EXPENSE { - condition = condition + " AND type=?" - conditionParams = append(conditionParams, transactionType) - } else if transactionType == models.TRANSACTION_DB_TYPE_TRANSFER_OUT || transactionType == models.TRANSACTION_DB_TYPE_TRANSFER_IN { - if accountId == 0 { - condition = condition + " AND type=?" - conditionParams = append(conditionParams, models.TRANSACTION_DB_TYPE_TRANSFER_OUT) - } else { - condition = condition + " AND (type=? OR type=?)" - conditionParams = append(conditionParams, models.TRANSACTION_DB_TYPE_TRANSFER_OUT) - conditionParams = append(conditionParams, models.TRANSACTION_DB_TYPE_TRANSFER_IN) - } - } else { - if noDuplicated && accountId == 0 { - condition = condition + " AND (type=? OR type=? OR type=? OR type=?)" - conditionParams = append(conditionParams, models.TRANSACTION_DB_TYPE_MODIFY_BALANCE) - conditionParams = append(conditionParams, models.TRANSACTION_DB_TYPE_INCOME) - conditionParams = append(conditionParams, models.TRANSACTION_DB_TYPE_EXPENSE) - conditionParams = append(conditionParams, models.TRANSACTION_DB_TYPE_TRANSFER_OUT) - } - } - - if len(categoryIds) > 0 { - var conditions strings.Builder - - for i := 0; i < len(categoryIds); i++ { - if i > 0 { - conditions.WriteString(",") - } - - conditions.WriteString("?") - conditionParams = append(conditionParams, categoryIds[i]) - } - - condition = condition + " AND category_id IN (" + conditions.String() + ")" - } - - if accountId > 0 { - condition = condition + " AND account_id=?" - conditionParams = append(conditionParams, accountId) - } - - if keyword != "" { - condition = condition + " AND comment LIKE ?" - conditionParams = append(conditionParams, "%%"+keyword+"%%") - } - - if maxTransactionTime > 0 { - condition = condition + " AND transaction_time<=?" - conditionParams = append(conditionParams, maxTransactionTime) - } - - if minTransactionTime > 0 { - condition = condition + " AND transaction_time>=?" - conditionParams = append(conditionParams, minTransactionTime) - } - + condition, conditionParams := s.getTransactionQueryCondition(uid, maxTransactionTime, minTransactionTime, transactionType, categoryIds, accountId, keyword, noDuplicated) err = s.UserDataDB(uid).Where(condition, conditionParams...).Limit(count, 0).OrderBy("transaction_time desc").Find(&transactions) return transactions, err @@ -164,65 +103,12 @@ func (s *TransactionService) GetTransactionsInMonthByPage(uid int64, year int, m endTime := startTime.AddDate(0, 1, 0) - startTransactionTime := utils.GetMinTransactionTimeFromUnixTime(startTime.Unix()) - endTransactionTime := utils.GetMinTransactionTimeFromUnixTime(endTime.Unix()) + minTransactionTime := utils.GetMinTransactionTimeFromUnixTime(startTime.Unix()) + maxTransactionTime := utils.GetMinTransactionTimeFromUnixTime(endTime.Unix()) - 1 var transactions []*models.Transaction - condition := "uid=? AND deleted=? AND transaction_time>=? AND transaction_time 0 { - var conditions strings.Builder - - for i := 0; i < len(categoryIds); i++ { - if i > 0 { - conditions.WriteString(",") - } - - conditions.WriteString("?") - conditionParams = append(conditionParams, categoryIds[i]) - } - - condition = condition + " AND category_id IN (" + conditions.String() + ")" - } - - if accountId > 0 { - condition = condition + " AND account_id=?" - conditionParams = append(conditionParams, accountId) - } - - if keyword != "" { - condition = condition + " AND comment LIKE ?" - conditionParams = append(conditionParams, "%%"+keyword+"%%") - } - + condition, conditionParams := s.getTransactionQueryCondition(uid, maxTransactionTime, minTransactionTime, transactionType, categoryIds, accountId, keyword, true) err = s.UserDataDB(uid).Where(condition, conditionParams...).Limit(count, count*(page-1)).OrderBy("transaction_time desc").Find(&transactions) return transactions, err @@ -252,11 +138,17 @@ func (s *TransactionService) GetTransactionByTransactionId(uid int64, transactio // GetAllTransactionCount returns total count of transactions func (s *TransactionService) GetAllTransactionCount(uid int64) (int64, error) { + return s.GetTransactionCount(uid, 0, 0, 0, nil, 0, "") +} + +// GetTransactionCount returns count of transactions +func (s *TransactionService) GetTransactionCount(uid int64, maxTransactionTime int64, minTransactionTime int64, transactionType models.TransactionDbType, categoryIds []int64, accountId int64, keyword string) (int64, error) { if uid <= 0 { return 0, errs.ErrUserIdInvalid } - return s.UserDataDB(uid).Where("uid=? AND deleted=?", uid, false).Count(&models.Transaction{}) + condition, conditionParams := s.getTransactionQueryCondition(uid, maxTransactionTime, minTransactionTime, transactionType, categoryIds, accountId, keyword, true) + return s.UserDataDB(uid).Where(condition, conditionParams...).Count(&models.Transaction{}) } // GetMonthTransactionCount returns total count of transactions in given year and month @@ -1122,6 +1014,72 @@ func (s *TransactionService) GetTransactionMapByList(transactions []*models.Tran return transactionMap } +func (s *TransactionService) getTransactionQueryCondition(uid int64, maxTransactionTime int64, minTransactionTime int64, transactionType models.TransactionDbType, categoryIds []int64, accountId int64, keyword string, noDuplicated bool) (string, []interface{}) { + condition := "uid=? AND deleted=?" + conditionParams := make([]interface{}, 0, 16) + conditionParams = append(conditionParams, uid) + conditionParams = append(conditionParams, false) + + if maxTransactionTime > 0 { + condition = condition + " AND transaction_time<=?" + conditionParams = append(conditionParams, maxTransactionTime) + } + + if minTransactionTime > 0 { + condition = condition + " AND transaction_time>=?" + conditionParams = append(conditionParams, minTransactionTime) + } + + if models.TRANSACTION_DB_TYPE_MODIFY_BALANCE <= transactionType && transactionType <= models.TRANSACTION_DB_TYPE_EXPENSE { + condition = condition + " AND type=?" + conditionParams = append(conditionParams, transactionType) + } else if transactionType == models.TRANSACTION_DB_TYPE_TRANSFER_OUT || transactionType == models.TRANSACTION_DB_TYPE_TRANSFER_IN { + if accountId == 0 { + condition = condition + " AND type=?" + conditionParams = append(conditionParams, models.TRANSACTION_DB_TYPE_TRANSFER_OUT) + } else { + condition = condition + " AND (type=? OR type=?)" + conditionParams = append(conditionParams, models.TRANSACTION_DB_TYPE_TRANSFER_OUT) + conditionParams = append(conditionParams, models.TRANSACTION_DB_TYPE_TRANSFER_IN) + } + } else { + if noDuplicated && accountId == 0 { + condition = condition + " AND (type=? OR type=? OR type=? OR type=?)" + conditionParams = append(conditionParams, models.TRANSACTION_DB_TYPE_MODIFY_BALANCE) + conditionParams = append(conditionParams, models.TRANSACTION_DB_TYPE_INCOME) + conditionParams = append(conditionParams, models.TRANSACTION_DB_TYPE_EXPENSE) + conditionParams = append(conditionParams, models.TRANSACTION_DB_TYPE_TRANSFER_OUT) + } + } + + if len(categoryIds) > 0 { + var conditions strings.Builder + + for i := 0; i < len(categoryIds); i++ { + if i > 0 { + conditions.WriteString(",") + } + + conditions.WriteString("?") + conditionParams = append(conditionParams, categoryIds[i]) + } + + condition = condition + " AND category_id IN (" + conditions.String() + ")" + } + + if accountId > 0 { + condition = condition + " AND account_id=?" + conditionParams = append(conditionParams, accountId) + } + + if keyword != "" { + condition = condition + " AND comment LIKE ?" + conditionParams = append(conditionParams, "%%"+keyword+"%%") + } + + return condition, conditionParams +} + func (s *TransactionService) isAccountIdValid(transaction *models.Transaction) error { if transaction.Type == models.TRANSACTION_DB_TYPE_MODIFY_BALANCE { if transaction.RelatedAccountId != 0 && transaction.RelatedAccountId != transaction.AccountId {