add category api
This commit is contained in:
+1
-1
@@ -31,7 +31,7 @@ func updateDatabaseStructure(c *cli.Context) error {
|
||||
|
||||
_ = datastore.Container.UserStore.SyncStructs(new(models.User), new(models.TwoFactor), new(models.TwoFactorRecoveryCode))
|
||||
_ = datastore.Container.TokenStore.SyncStructs(new(models.TokenRecord))
|
||||
_ = datastore.Container.UserDataStore.SyncStructs(new(models.Account))
|
||||
_ = datastore.Container.UserDataStore.SyncStructs(new(models.Account), new(models.TransactionCategory))
|
||||
|
||||
log.BootInfof("[database.updateDatabaseStructure] maintained successfully")
|
||||
|
||||
|
||||
@@ -176,6 +176,15 @@ func startWebServer(c *cli.Context) error {
|
||||
apiV1Route.POST("/accounts/move.json", bindApi(api.Accounts.AccountMoveHandler))
|
||||
apiV1Route.POST("/accounts/delete.json", bindApi(api.Accounts.AccountDeleteHandler))
|
||||
|
||||
// Transaction Categories
|
||||
apiV1Route.GET("/transaction/categories/list.json", bindApi(api.TransactionCategories.CategoryListHandler))
|
||||
apiV1Route.GET("/transaction/categories/get.json", bindApi(api.TransactionCategories.CategoryGetHandler))
|
||||
apiV1Route.POST("/transaction/categories/add.json", bindApi(api.TransactionCategories.CategoryCreateHandler))
|
||||
apiV1Route.POST("/transaction/categories/modify.json", bindApi(api.TransactionCategories.CategoryModifyHandler))
|
||||
apiV1Route.POST("/transaction/categories/hide.json", bindApi(api.TransactionCategories.CategoryHideHandler))
|
||||
apiV1Route.POST("/transaction/categories/move.json", bindApi(api.TransactionCategories.CategoryMoveHandler))
|
||||
apiV1Route.POST("/transaction/categories/delete.json", bindApi(api.TransactionCategories.CategoryDeleteHandler))
|
||||
|
||||
// Exchange Rates
|
||||
apiV1Route.GET("/exchange_rates/latest.json", bindApi(api.ExchangeRates.LatestExchangeRateHandler))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,291 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
type TransactionCategoriesApi struct {
|
||||
categories *services.TransactionCategoryService
|
||||
}
|
||||
|
||||
var (
|
||||
TransactionCategories = &TransactionCategoriesApi{
|
||||
categories: services.TransactionCategories,
|
||||
}
|
||||
)
|
||||
|
||||
func (a *TransactionCategoriesApi) CategoryListHandler(c *core.Context) (interface{}, *errs.Error) {
|
||||
var categoryListReq models.TransactionCategoryListRequest
|
||||
err := c.ShouldBindQuery(&categoryListReq)
|
||||
|
||||
if err != nil {
|
||||
log.WarnfWithRequestId(c, "[transaction_categories.CategoryListHandler] parse request failed, because %s", err.Error())
|
||||
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
||||
}
|
||||
|
||||
uid := c.GetCurrentUid()
|
||||
categories, err := a.categories.GetAllCategoriesByUid(uid, categoryListReq.Type, categoryListReq.ParentId)
|
||||
|
||||
if err != nil {
|
||||
log.ErrorfWithRequestId(c, "[transaction_categories.CategoryListHandler] failed to get categories for user \"uid:%d\", because %s", uid, err.Error())
|
||||
return nil, errs.ErrOperationFailed
|
||||
}
|
||||
|
||||
categoryResps := make([]*models.TransactionCategoryInfoResponse, len(categories))
|
||||
categoryRespMap := make(map[int64]*models.TransactionCategoryInfoResponse)
|
||||
|
||||
for i := 0; i < len(categories); i++ {
|
||||
categoryResps[i] = categories[i].ToTransactionCategoryInfoResponse()
|
||||
categoryRespMap[categoryResps[i].Id] = categoryResps[i]
|
||||
}
|
||||
|
||||
for i := 0; i < len(categoryResps); i++ {
|
||||
categoryResp := categoryResps[i]
|
||||
|
||||
if categoryResp.ParentId <= models.TRANSACTION_PARENT_ID_LEVEL_ONE {
|
||||
continue
|
||||
}
|
||||
|
||||
parentCategory, parentExists := categoryRespMap[categoryResp.ParentId]
|
||||
|
||||
if !parentExists || parentCategory == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
parentCategory.SubCategories = append(parentCategory.SubCategories, categoryResp)
|
||||
}
|
||||
|
||||
finalCategoryResps := make(models.TransactionCategoryInfoResponseSlice, 0)
|
||||
|
||||
for i := 0; i < len(categoryResps); i++ {
|
||||
if categoryListReq.ParentId <= 0 && categoryResps[i].ParentId == models.TRANSACTION_PARENT_ID_LEVEL_ONE {
|
||||
sort.Sort(categoryResps[i].SubCategories)
|
||||
finalCategoryResps = append(finalCategoryResps, categoryResps[i])
|
||||
} else if categoryListReq.ParentId > 0 && categoryResps[i].ParentId == categoryListReq.ParentId {
|
||||
finalCategoryResps = append(finalCategoryResps, categoryResps[i])
|
||||
}
|
||||
}
|
||||
|
||||
sort.Sort(finalCategoryResps)
|
||||
|
||||
typeCategoryMapResponse := make(map[models.TransactionCategoryType]models.TransactionCategoryInfoResponseSlice)
|
||||
|
||||
for i := 0; i < len(finalCategoryResps); i++ {
|
||||
category := finalCategoryResps[i]
|
||||
categoryList, _ := typeCategoryMapResponse[category.Type]
|
||||
|
||||
categoryList = append(categoryList, category)
|
||||
typeCategoryMapResponse[category.Type] = categoryList
|
||||
}
|
||||
|
||||
return typeCategoryMapResponse, nil
|
||||
}
|
||||
|
||||
func (a *TransactionCategoriesApi) CategoryGetHandler(c *core.Context) (interface{}, *errs.Error) {
|
||||
var categoryGetReq models.TransactionCategoryGetRequest
|
||||
err := c.ShouldBindQuery(&categoryGetReq)
|
||||
|
||||
if err != nil {
|
||||
log.WarnfWithRequestId(c, "[transaction_categories.CategoryGetHandler] parse request failed, because %s", err.Error())
|
||||
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
||||
}
|
||||
|
||||
uid := c.GetCurrentUid()
|
||||
category, err := a.categories.GetCategoryByCategoryId(uid, categoryGetReq.Id)
|
||||
|
||||
if err != nil {
|
||||
log.ErrorfWithRequestId(c, "[transaction_categories.CategoryGetHandler] failed to get category \"id:%d\" for user \"uid:%d\", because %s", categoryGetReq.Id, uid, err.Error())
|
||||
return nil, errs.ErrOperationFailed
|
||||
}
|
||||
|
||||
categoryResp := category.ToTransactionCategoryInfoResponse()
|
||||
|
||||
return categoryResp, nil
|
||||
}
|
||||
|
||||
func (a *TransactionCategoriesApi) CategoryCreateHandler(c *core.Context) (interface{}, *errs.Error) {
|
||||
var categoryCreateReq models.TransactionCategoryCreateRequest
|
||||
err := c.ShouldBindJSON(&categoryCreateReq)
|
||||
|
||||
if err != nil {
|
||||
log.WarnfWithRequestId(c, "[transaction_categories.CategoryCreateHandler] parse request failed, because %s", err.Error())
|
||||
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
||||
}
|
||||
|
||||
if categoryCreateReq.Type < models.CATEGORY_TYPE_INCOME || categoryCreateReq.Type > models.CATEGORY_TYPE_TRANSFER {
|
||||
log.WarnfWithRequestId(c, "[transaction_categories.CategoryCreateHandler] category type invalid, type is %d", categoryCreateReq.Type)
|
||||
return nil, errs.ErrTransactionCategoryTypeInvalid
|
||||
}
|
||||
|
||||
uid := c.GetCurrentUid()
|
||||
|
||||
var maxOrderId = 0
|
||||
|
||||
if categoryCreateReq.ParentId <= 0 {
|
||||
maxOrderId, err = a.categories.GetMaxDisplayOrder(uid, categoryCreateReq.Type)
|
||||
} else {
|
||||
maxOrderId, err = a.categories.GetMaxSubCategoryDisplayOrder(uid, categoryCreateReq.Type, categoryCreateReq.ParentId)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.ErrorfWithRequestId(c, "[transaction_categories.CategoryCreateHandler] failed to get max display order for user \"uid:%d\", because %s", uid, err.Error())
|
||||
return nil, errs.ErrOperationFailed
|
||||
}
|
||||
|
||||
category := a.createNewCategory(uid, &categoryCreateReq, maxOrderId+1)
|
||||
|
||||
err = a.categories.CreateCategory(category)
|
||||
|
||||
if err != nil {
|
||||
log.ErrorfWithRequestId(c, "[transaction_categories.CategoryCreateHandler] failed to create category \"id:%d\" for user \"uid:%d\", because %s", category.CategoryId, uid, err.Error())
|
||||
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||
}
|
||||
|
||||
log.InfofWithRequestId(c, "[transaction_categories.CategoryCreateHandler] user \"uid:%d\" has created a new category \"id:%d\" successfully", uid, category.CategoryId)
|
||||
|
||||
categoryResp := category.ToTransactionCategoryInfoResponse()
|
||||
|
||||
return categoryResp, nil
|
||||
}
|
||||
|
||||
func (a *TransactionCategoriesApi) CategoryModifyHandler(c *core.Context) (interface{}, *errs.Error) {
|
||||
var categoryModifyReq models.TransactionCategoryModifyRequest
|
||||
err := c.ShouldBindJSON(&categoryModifyReq)
|
||||
|
||||
if err != nil {
|
||||
log.WarnfWithRequestId(c, "[transaction_categories.CategoryModifyHandler] parse request failed, because %s", err.Error())
|
||||
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
||||
}
|
||||
|
||||
uid := c.GetCurrentUid()
|
||||
category, err := a.categories.GetCategoryByCategoryId(uid, categoryModifyReq.Id)
|
||||
|
||||
if err != nil {
|
||||
log.ErrorfWithRequestId(c, "[transaction_categories.CategoryModifyHandler] failed to get category \"id:%d\" for user \"uid:%d\", because %s", categoryModifyReq.Id, uid, err.Error())
|
||||
return nil, errs.ErrOperationFailed
|
||||
}
|
||||
|
||||
newCategory := &models.TransactionCategory{
|
||||
CategoryId: category.CategoryId,
|
||||
Uid: uid,
|
||||
Name: categoryModifyReq.Name,
|
||||
Icon: categoryModifyReq.Icon,
|
||||
Color: categoryModifyReq.Color,
|
||||
Comment: categoryModifyReq.Comment,
|
||||
Hidden: categoryModifyReq.Hidden,
|
||||
}
|
||||
|
||||
if newCategory.Name == category.Name &&
|
||||
newCategory.Icon == category.Icon &&
|
||||
newCategory.Color == category.Color &&
|
||||
newCategory.Comment == category.Comment &&
|
||||
newCategory.Hidden == category.Hidden {
|
||||
return nil, errs.ErrNothingWillBeUpdated
|
||||
}
|
||||
|
||||
err = a.categories.ModifyCategory(newCategory)
|
||||
|
||||
if err != nil {
|
||||
log.ErrorfWithRequestId(c, "[transaction_categories.CategoryModifyHandler] failed to update category \"id:%d\" for user \"uid:%d\", because %s", categoryModifyReq.Id, uid, err.Error())
|
||||
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||
}
|
||||
|
||||
log.InfofWithRequestId(c, "[transaction_categories.CategoryModifyHandler] user \"uid:%d\" has updated category \"id:%d\" successfully", uid, categoryModifyReq.Id)
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (a *TransactionCategoriesApi) CategoryHideHandler(c *core.Context) (interface{}, *errs.Error) {
|
||||
var categoryHideReq models.TransactionCategoryHideRequest
|
||||
err := c.ShouldBindJSON(&categoryHideReq)
|
||||
|
||||
if err != nil {
|
||||
log.WarnfWithRequestId(c, "[transaction_categories.CategoryHideHandler] parse request failed, because %s", err.Error())
|
||||
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
||||
}
|
||||
|
||||
uid := c.GetCurrentUid()
|
||||
err = a.categories.HideCategory(uid, []int64{categoryHideReq.Id}, categoryHideReq.Hidden)
|
||||
|
||||
if err != nil {
|
||||
log.ErrorfWithRequestId(c, "[transaction_categories.CategoryHideHandler] failed to hide category \"id:%d\" for user \"uid:%d\", because %s", categoryHideReq.Id, uid, err.Error())
|
||||
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||
}
|
||||
|
||||
log.InfofWithRequestId(c, "[transaction_categories.CategoryHideHandler] user \"uid:%d\" has hidden category \"id:%d\"", uid, categoryHideReq.Id)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (a *TransactionCategoriesApi) CategoryMoveHandler(c *core.Context) (interface{}, *errs.Error) {
|
||||
var categoryMoveReq models.TransactionCategoryMoveRequest
|
||||
err := c.ShouldBindJSON(&categoryMoveReq)
|
||||
|
||||
if err != nil {
|
||||
log.WarnfWithRequestId(c, "[transaction_categories.CategoryMoveHandler] parse request failed, because %s", err.Error())
|
||||
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
||||
}
|
||||
|
||||
uid := c.GetCurrentUid()
|
||||
categories := make([]*models.TransactionCategory, len(categoryMoveReq.NewDisplayOrders))
|
||||
|
||||
for i := 0; i < len(categoryMoveReq.NewDisplayOrders); i++ {
|
||||
newDisplayOrder := categoryMoveReq.NewDisplayOrders[i]
|
||||
category := &models.TransactionCategory{
|
||||
Uid: uid,
|
||||
CategoryId: newDisplayOrder.Id,
|
||||
DisplayOrder: newDisplayOrder.DisplayOrder,
|
||||
}
|
||||
|
||||
categories[i] = category
|
||||
}
|
||||
|
||||
err = a.categories.ModifyCategoryDisplayOrders(uid, categories)
|
||||
|
||||
if err != nil {
|
||||
log.ErrorfWithRequestId(c, "[transaction_categories.CategoryMoveHandler] failed to move categories for user \"uid:%d\", because %s", uid, err.Error())
|
||||
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||
}
|
||||
|
||||
log.InfofWithRequestId(c, "[transaction_categories.CategoryMoveHandler] user \"uid:%d\" has moved categories", uid)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (a *TransactionCategoriesApi) CategoryDeleteHandler(c *core.Context) (interface{}, *errs.Error) {
|
||||
var categoryDeleteReq models.TransactionCategoryDeleteRequest
|
||||
err := c.ShouldBindJSON(&categoryDeleteReq)
|
||||
|
||||
if err != nil {
|
||||
log.WarnfWithRequestId(c, "[transaction_categories.CategoryDeleteHandler] parse request failed, because %s", err.Error())
|
||||
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
||||
}
|
||||
|
||||
uid := c.GetCurrentUid()
|
||||
err = a.categories.DeleteCategories(uid, []int64{categoryDeleteReq.Id})
|
||||
|
||||
if err != nil {
|
||||
log.ErrorfWithRequestId(c, "[transaction_categories.CategoryDeleteHandler] failed to delete category \"id:%d\" for user \"uid:%d\", because %s", categoryDeleteReq.Id, uid, err.Error())
|
||||
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||
}
|
||||
|
||||
log.InfofWithRequestId(c, "[transaction_categories.CategoryDeleteHandler] user \"uid:%d\" has deleted category \"id:%d\"", uid, categoryDeleteReq.Id)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (a *TransactionCategoriesApi) createNewCategory(uid int64, categoryCreateReq *models.TransactionCategoryCreateRequest, order int) *models.TransactionCategory {
|
||||
return &models.TransactionCategory{
|
||||
Uid: uid,
|
||||
Name: categoryCreateReq.Name,
|
||||
Type: categoryCreateReq.Type,
|
||||
ParentCategoryId: categoryCreateReq.ParentId,
|
||||
DisplayOrder: order,
|
||||
Icon: categoryCreateReq.Icon,
|
||||
Color: categoryCreateReq.Color,
|
||||
Comment: categoryCreateReq.Comment,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package errs
|
||||
|
||||
import "net/http"
|
||||
|
||||
var (
|
||||
ErrTransactionCategoryIdInvalid = NewNormalError(NORMAL_SUBCATEGORY_CATEGORY, 0, http.StatusBadRequest, "transaction category id is invalid")
|
||||
ErrTransactionCategoryNotFound = NewNormalError(NORMAL_SUBCATEGORY_CATEGORY, 1, http.StatusBadRequest, "transaction category not found")
|
||||
ErrTransactionCategoryTypeInvalid = NewNormalError(NORMAL_SUBCATEGORY_CATEGORY, 2, http.StatusBadRequest, "transaction category type is invalid")
|
||||
)
|
||||
@@ -0,0 +1,115 @@
|
||||
package models
|
||||
|
||||
// Level-One Transaction Category
|
||||
const TRANSACTION_PARENT_ID_LEVEL_ONE = 0
|
||||
|
||||
type TransactionCategoryType byte
|
||||
|
||||
const (
|
||||
CATEGORY_TYPE_INCOME TransactionCategoryType = 1
|
||||
CATEGORY_TYPE_EXPENSE TransactionCategoryType = 2
|
||||
CATEGORY_TYPE_TRANSFER TransactionCategoryType = 3
|
||||
)
|
||||
|
||||
type TransactionCategory struct {
|
||||
CategoryId int64 `xorm:"PK"`
|
||||
Uid int64 `xorm:"INDEX(IDX_category_uid_deleted_type_parent_category_id_order) NOT NULL"`
|
||||
Deleted bool `xorm:"INDEX(IDX_category_uid_deleted_type_parent_category_id_order) NOT NULL"`
|
||||
Type TransactionCategoryType `xorm:"INDEX(IDX_category_uid_deleted_type_parent_category_id_order) NOT NULL"`
|
||||
ParentCategoryId int64 `xorm:"INDEX(IDX_category_uid_deleted_type_parent_category_id_order) NOT NULL"`
|
||||
Name string `xorm:"VARCHAR(32) NOT NULL"`
|
||||
DisplayOrder int `xorm:"INDEX(IDX_category_uid_deleted_type_parent_category_id_order) NOT NULL"`
|
||||
Icon int64 `xorm:"NOT NULL"`
|
||||
Color string `xorm:"VARCHAR(6) NOT NULL"`
|
||||
Hidden bool `xorm:"NOT NULL"`
|
||||
Comment string `xorm:"VARCHAR(255) NOT NULL"`
|
||||
CreatedUnixTime int64
|
||||
UpdatedUnixTime int64
|
||||
DeletedUnixTime int64
|
||||
}
|
||||
|
||||
type TransactionCategoryCreateRequest struct {
|
||||
Name string `json:"name" binding:"required,notBlank,max=32"`
|
||||
Type TransactionCategoryType `json:"type" binding:"required"`
|
||||
ParentId int64 `json:"parentId,string" binding:"min=0"`
|
||||
Icon int64 `json:"icon,string" binding:"min=1"`
|
||||
Color string `json:"color" binding:"required,len=6,validHexRGBColor"`
|
||||
Comment string `json:"comment" binding:"max=255"`
|
||||
}
|
||||
|
||||
type TransactionCategoryListRequest struct {
|
||||
Type TransactionCategoryType `form:"type" binding:"min=0"`
|
||||
ParentId int64 `form:"parent_id,string,default=-1" binding:"min=-1"`
|
||||
}
|
||||
|
||||
type TransactionCategoryGetRequest struct {
|
||||
Id int64 `form:"id,string" binding:"required,min=1"`
|
||||
}
|
||||
|
||||
type TransactionCategoryModifyRequest struct {
|
||||
Id int64 `json:"id,string" binding:"required,min=1"`
|
||||
Name string `json:"name" binding:"required,notBlank,max=32"`
|
||||
Icon int64 `json:"icon,string" binding:"min=1"`
|
||||
Color string `json:"color" binding:"required,len=6,validHexRGBColor"`
|
||||
Comment string `json:"comment" binding:"max=255"`
|
||||
Hidden bool `json:"hidden"`
|
||||
}
|
||||
|
||||
type TransactionCategoryHideRequest struct {
|
||||
Id int64 `json:"id,string" binding:"required,min=1"`
|
||||
Hidden bool `json:"hidden"`
|
||||
}
|
||||
|
||||
type TransactionCategoryMoveRequest struct {
|
||||
NewDisplayOrders []*TransactionCategoryNewDisplayOrderRequest `json:"newDisplayOrders"`
|
||||
}
|
||||
|
||||
type TransactionCategoryNewDisplayOrderRequest struct {
|
||||
Id int64 `json:"id,string" binding:"required,min=1"`
|
||||
DisplayOrder int `json:"displayOrder"`
|
||||
}
|
||||
|
||||
type TransactionCategoryDeleteRequest struct {
|
||||
Id int64 `json:"id,string" binding:"required,min=1"`
|
||||
}
|
||||
|
||||
type TransactionCategoryInfoResponse struct {
|
||||
Id int64 `json:"id,string"`
|
||||
Name string `json:"name"`
|
||||
ParentId int64 `json:"parentId,string"`
|
||||
Type TransactionCategoryType `json:"type"`
|
||||
Icon int64 `json:"icon,string"`
|
||||
Color string `json:"color"`
|
||||
Comment string `json:"comment"`
|
||||
DisplayOrder int `json:"displayOrder"`
|
||||
Hidden bool `json:"hidden"`
|
||||
SubCategories TransactionCategoryInfoResponseSlice `json:"subCategories,omitempty"`
|
||||
}
|
||||
|
||||
func (c *TransactionCategory) ToTransactionCategoryInfoResponse() *TransactionCategoryInfoResponse {
|
||||
return &TransactionCategoryInfoResponse{
|
||||
Id: c.CategoryId,
|
||||
Name: c.Name,
|
||||
ParentId: c.ParentCategoryId,
|
||||
Type: c.Type,
|
||||
Icon: c.Icon,
|
||||
Color: c.Color,
|
||||
Comment: c.Comment,
|
||||
DisplayOrder: c.DisplayOrder,
|
||||
Hidden: c.Hidden,
|
||||
}
|
||||
}
|
||||
|
||||
type TransactionCategoryInfoResponseSlice []*TransactionCategoryInfoResponse
|
||||
|
||||
func (c TransactionCategoryInfoResponseSlice) Len() int {
|
||||
return len(c)
|
||||
}
|
||||
|
||||
func (c TransactionCategoryInfoResponseSlice) Swap(i, j int) {
|
||||
c[i], c[j] = c[j], c[i]
|
||||
}
|
||||
|
||||
func (c TransactionCategoryInfoResponseSlice) Less(i, j int) bool {
|
||||
return c[i].DisplayOrder < c[j].DisplayOrder
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"xorm.io/xorm"
|
||||
|
||||
"github.com/mayswind/lab/pkg/datastore"
|
||||
"github.com/mayswind/lab/pkg/errs"
|
||||
"github.com/mayswind/lab/pkg/models"
|
||||
"github.com/mayswind/lab/pkg/uuid"
|
||||
)
|
||||
|
||||
type TransactionCategoryService struct {
|
||||
ServiceUsingDB
|
||||
ServiceUsingUuid
|
||||
}
|
||||
|
||||
var (
|
||||
TransactionCategories = &TransactionCategoryService{
|
||||
ServiceUsingDB: ServiceUsingDB{
|
||||
container: datastore.Container,
|
||||
},
|
||||
ServiceUsingUuid: ServiceUsingUuid{
|
||||
container: uuid.Container,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func (s *TransactionCategoryService) GetAllCategoriesByUid(uid int64, categoryType models.TransactionCategoryType, parentCategoryId int64) ([]*models.TransactionCategory, error) {
|
||||
if uid <= 0 {
|
||||
return nil, errs.ErrUserIdInvalid
|
||||
}
|
||||
|
||||
condition := "uid=? AND deleted=?"
|
||||
var conditionParams []interface{}
|
||||
conditionParams = append(conditionParams, uid)
|
||||
conditionParams = append(conditionParams, false)
|
||||
|
||||
if categoryType > 0 {
|
||||
condition = condition + " AND type=?"
|
||||
conditionParams = append(conditionParams, categoryType)
|
||||
}
|
||||
|
||||
if parentCategoryId >= 0 {
|
||||
condition = condition + " AND parent_category_id=?"
|
||||
conditionParams = append(conditionParams, parentCategoryId)
|
||||
}
|
||||
|
||||
var categories []*models.TransactionCategory
|
||||
err := s.UserDataDB(uid).Where(condition, conditionParams...).OrderBy("type asc, parent_category_id asc, display_order asc").Find(&categories)
|
||||
|
||||
return categories, err
|
||||
}
|
||||
|
||||
func (s *TransactionCategoryService) GetCategoryByCategoryId(uid int64, categoryId int64) (*models.TransactionCategory, error) {
|
||||
if uid <= 0 {
|
||||
return nil, errs.ErrUserIdInvalid
|
||||
}
|
||||
|
||||
if categoryId <= 0 {
|
||||
return nil, errs.ErrTransactionCategoryIdInvalid
|
||||
}
|
||||
|
||||
category := &models.TransactionCategory{}
|
||||
has, err := s.UserDataDB(uid).Where("uid=? AND deleted=? AND category_id=?", uid, false, categoryId).Get(category)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if has {
|
||||
return category, nil
|
||||
} else {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *TransactionCategoryService) GetCategoryAndSubCategoriesByCategoryId(uid int64, categoryId int64) ([]*models.TransactionCategory, error) {
|
||||
if uid <= 0 {
|
||||
return nil, errs.ErrUserIdInvalid
|
||||
}
|
||||
|
||||
if categoryId <= 0 {
|
||||
return nil, errs.ErrTransactionCategoryIdInvalid
|
||||
}
|
||||
|
||||
var categories []*models.TransactionCategory
|
||||
err := s.UserDataDB(uid).Where("uid=? AND deleted=? AND (category_id=? OR parent_category_id=?)", uid, false, categoryId, categoryId).OrderBy("type asc, parent_category_id asc, display_order asc").Find(&categories)
|
||||
|
||||
return categories, err
|
||||
}
|
||||
|
||||
func (s *TransactionCategoryService) GetMaxDisplayOrder(uid int64, categoryType models.TransactionCategoryType) (int, error) {
|
||||
if uid <= 0 {
|
||||
return 0, errs.ErrUserIdInvalid
|
||||
}
|
||||
|
||||
category := &models.TransactionCategory{}
|
||||
has, err := s.UserDataDB(uid).Cols("uid", "deleted", "parent_category_id", "display_order").Where("uid=? AND deleted=? AND type=? AND parent_category_id=?", uid, false, categoryType, models.TRANSACTION_PARENT_ID_LEVEL_ONE).OrderBy("display_order desc").Limit(1).Get(category)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if has {
|
||||
return category.DisplayOrder, nil
|
||||
} else {
|
||||
return 0, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *TransactionCategoryService) GetMaxSubCategoryDisplayOrder(uid int64, categoryType models.TransactionCategoryType, parentCategoryId int64) (int, error) {
|
||||
if uid <= 0 {
|
||||
return 0, errs.ErrUserIdInvalid
|
||||
}
|
||||
|
||||
if parentCategoryId <= 0 {
|
||||
return 0, errs.ErrTransactionCategoryIdInvalid
|
||||
}
|
||||
|
||||
category := &models.TransactionCategory{}
|
||||
has, err := s.UserDataDB(uid).Cols("uid", "deleted", "parent_category_id", "display_order").Where("uid=? AND deleted=? AND type=? AND parent_category_id=?", uid, false, categoryType, parentCategoryId).OrderBy("display_order desc").Limit(1).Get(category)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if has {
|
||||
return category.DisplayOrder, nil
|
||||
} else {
|
||||
return 0, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *TransactionCategoryService) CreateCategory(category *models.TransactionCategory) error {
|
||||
if category.Uid <= 0 {
|
||||
return errs.ErrUserIdInvalid
|
||||
}
|
||||
|
||||
category.CategoryId = s.GenerateUuid(uuid.UUID_TYPE_CATEGORY)
|
||||
|
||||
category.Deleted = false
|
||||
category.CreatedUnixTime = time.Now().Unix()
|
||||
category.UpdatedUnixTime = time.Now().Unix()
|
||||
|
||||
return s.UserDataDB(category.Uid).DoTransaction(func(sess *xorm.Session) error {
|
||||
_, err := sess.Insert(category)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func (s *TransactionCategoryService) ModifyCategory(category *models.TransactionCategory) error {
|
||||
if category.Uid <= 0 {
|
||||
return errs.ErrUserIdInvalid
|
||||
}
|
||||
|
||||
category.UpdatedUnixTime = time.Now().Unix()
|
||||
|
||||
return s.UserDataDB(category.Uid).DoTransaction(func(sess *xorm.Session) error {
|
||||
updatedRows, err := sess.Cols("name", "icon", "color", "comment", "hidden", "updated_unix_time").Where("category_id=? AND uid=? AND deleted=?", category.CategoryId, category.Uid, false).Update(category)
|
||||
|
||||
if err != nil {
|
||||
return errs.ErrDatabaseOperationFailed
|
||||
}
|
||||
|
||||
if updatedRows < 1 {
|
||||
return errs.ErrTransactionCategoryNotFound
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (s *TransactionCategoryService) HideCategory(uid int64, ids []int64, hidden bool) error {
|
||||
if uid <= 0 {
|
||||
return errs.ErrUserIdInvalid
|
||||
}
|
||||
|
||||
now := time.Now().Unix()
|
||||
|
||||
updateModel := &models.TransactionCategory{
|
||||
Hidden: hidden,
|
||||
UpdatedUnixTime: now,
|
||||
}
|
||||
|
||||
return s.UserDataDB(uid).DoTransaction(func(sess *xorm.Session) error {
|
||||
deletedRows, err := sess.Cols("hidden", "updated_unix_time").In("category_id", ids).Where("uid=? AND deleted=?", uid, false).Update(updateModel)
|
||||
|
||||
if deletedRows < 1 {
|
||||
return errs.ErrTransactionCategoryNotFound
|
||||
}
|
||||
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func (s *TransactionCategoryService) ModifyCategoryDisplayOrders(uid int64, categories []*models.TransactionCategory) error {
|
||||
if uid <= 0 {
|
||||
return errs.ErrUserIdInvalid
|
||||
}
|
||||
|
||||
for i := 0; i < len(categories); i++ {
|
||||
categories[i].UpdatedUnixTime = time.Now().Unix()
|
||||
}
|
||||
|
||||
return s.UserDataDB(uid).DoTransaction(func(sess *xorm.Session) error {
|
||||
for i := 0; i < len(categories); i++ {
|
||||
category := categories[i]
|
||||
_, err := sess.Cols("display_order", "updated_unix_time").Where("category_id=? AND uid=? AND deleted=?", category.CategoryId, uid, false).Update(category)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (s *TransactionCategoryService) DeleteCategories(uid int64, ids []int64) error {
|
||||
if uid <= 0 {
|
||||
return errs.ErrUserIdInvalid
|
||||
}
|
||||
|
||||
now := time.Now().Unix()
|
||||
|
||||
updateModel := &models.TransactionCategory{
|
||||
Deleted: true,
|
||||
DeletedUnixTime: now,
|
||||
}
|
||||
|
||||
return s.UserDataDB(uid).DoTransaction(func(sess *xorm.Session) error {
|
||||
deletedRows, err := sess.Cols("deleted", "deleted_unix_time").In("category_id", ids).Where("uid=? AND deleted=?", uid, false).Update(updateModel)
|
||||
|
||||
if deletedRows < 1 {
|
||||
return errs.ErrTransactionCategoryNotFound
|
||||
}
|
||||
|
||||
_, err = sess.Cols("deleted", "deleted_unix_time").In("parent_category_id", ids).Where("uid=? AND deleted=?", uid, false).Update(updateModel)
|
||||
|
||||
return err
|
||||
})
|
||||
}
|
||||
@@ -212,6 +212,48 @@ export default {
|
||||
id
|
||||
});
|
||||
},
|
||||
getAllTransactionCategories: ({ type, parentId }) => {
|
||||
return axios.get('v1/transaction/categories/list.json?type=' + (type || '0') + '&parent_id=' + (parentId || parentId === 0 ? parentId : '-1'));
|
||||
},
|
||||
getTransactionCategory: ({ id }) => {
|
||||
return axios.get('v1/transaction/categories/get.json?id=' + id);
|
||||
},
|
||||
addTransactionCategory: ({ name, type, parentId, icon, color, comment }) => {
|
||||
return axios.post('v1/transaction/categories/add.json', {
|
||||
name,
|
||||
type,
|
||||
parentId,
|
||||
icon,
|
||||
color,
|
||||
comment
|
||||
});
|
||||
},
|
||||
modifyTransactionCategory: ({ id, name, icon, color, comment, hidden }) => {
|
||||
return axios.post('v1/transaction/categories/modify.json', {
|
||||
id,
|
||||
name,
|
||||
icon,
|
||||
color,
|
||||
comment,
|
||||
hidden
|
||||
});
|
||||
},
|
||||
hideTransactionCategory: ({ id, hidden }) => {
|
||||
return axios.post('v1/transaction/categories/hide.json', {
|
||||
id,
|
||||
hidden
|
||||
});
|
||||
},
|
||||
moveTransactionCategory: ({ newDisplayOrders }) => {
|
||||
return axios.post('v1/transaction/categories/move.json', {
|
||||
newDisplayOrders,
|
||||
});
|
||||
},
|
||||
deleteTransactionCategory: ({ id }) => {
|
||||
return axios.post('v1/transaction/categories/delete.json', {
|
||||
id
|
||||
});
|
||||
},
|
||||
getLatestExchangeRates: () => {
|
||||
return axios.get('v1/exchange_rates/latest.json');
|
||||
},
|
||||
|
||||
@@ -221,6 +221,9 @@ export default {
|
||||
'sub account category not equals to parent': 'Sub account category does not equal to parent',
|
||||
'sub account type invalid': 'Sub account type is invalid',
|
||||
'cannot add or delete sub accounts when modify account': 'You cannot add or delete sub accounts when modify account',
|
||||
'transaction category id is invalid': 'Transaction category ID is invalid',
|
||||
'transaction category not found': 'Transaction category is not found',
|
||||
'transaction category type is invalid': 'Transaction category type is invalid',
|
||||
},
|
||||
'parameter': {
|
||||
'id': 'ID',
|
||||
@@ -236,6 +239,7 @@ export default {
|
||||
'type': 'Type',
|
||||
'color': 'Color',
|
||||
'currency': 'Currency',
|
||||
'parentId': 'Parent Node ID',
|
||||
'comment': 'Comment',
|
||||
},
|
||||
'parameterizedError': {
|
||||
|
||||
@@ -221,6 +221,9 @@ export default {
|
||||
'sub account category not equals to parent': '子账户类别与父账户不同',
|
||||
'sub account type invalid': '子账户类型无效',
|
||||
'cannot add or delete sub accounts when modify account': '您不能在修改账户时添加或删除子账户',
|
||||
'transaction category id is invalid': '交易分类ID无效',
|
||||
'transaction category not found': '交易分类不存在',
|
||||
'transaction category type is invalid': '交易分类类型无效',
|
||||
},
|
||||
'parameter': {
|
||||
'id': 'ID',
|
||||
@@ -236,6 +239,7 @@ export default {
|
||||
'type': '类型',
|
||||
'color': '颜色',
|
||||
'currency': '货币',
|
||||
'parentId': '父节点ID',
|
||||
'comment': '备注',
|
||||
},
|
||||
'parameterizedError': {
|
||||
|
||||
Reference in New Issue
Block a user