support transaction tag group

This commit is contained in:
MaysWind
2026-01-17 00:47:51 +08:00
parent b556efa510
commit 7d9cfc4ced
59 changed files with 3289 additions and 795 deletions
+9
View File
@@ -28,6 +28,7 @@ type DataManagementsApi struct {
transactions *services.TransactionService
categories *services.TransactionCategoryService
tags *services.TransactionTagService
tagGroups *services.TransactionTagGroupService
pictures *services.TransactionPictureService
templates *services.TransactionTemplateService
userCustomExchangeRates *services.UserCustomExchangeRatesService
@@ -46,6 +47,7 @@ var (
transactions: services.Transactions,
categories: services.TransactionCategories,
tags: services.TransactionTags,
tagGroups: services.TransactionTagGroups,
pictures: services.TransactionPictures,
templates: services.TransactionTemplates,
userCustomExchangeRates: services.UserCustomExchangeRates,
@@ -193,6 +195,13 @@ func (a *DataManagementsApi) ClearAllDataHandler(c *core.WebContext) (any, *errs
return nil, errs.Or(err, errs.ErrOperationFailed)
}
err = a.tagGroups.DeleteAllTagGroups(c, uid)
if err != nil {
log.Errorf(c, "[data_managements.ClearAllDataHandler] failed to delete all transaction tag groups, because %s", err.Error())
return nil, errs.Or(err, errs.ErrOperationFailed)
}
err = a.userCustomExchangeRates.DeleteAllCustomExchangeRates(c, uid)
if err != nil {
+210
View File
@@ -0,0 +1,210 @@
package api
import (
"sort"
"github.com/mayswind/ezbookkeeping/pkg/core"
"github.com/mayswind/ezbookkeeping/pkg/errs"
"github.com/mayswind/ezbookkeeping/pkg/log"
"github.com/mayswind/ezbookkeeping/pkg/models"
"github.com/mayswind/ezbookkeeping/pkg/services"
)
// TransactionTagGroupsApi represents transaction tag group api
type TransactionTagGroupsApi struct {
tagGroups *services.TransactionTagGroupService
}
// Initialize a transaction tag group api singleton instance
var (
TransactionTagGroups = &TransactionTagGroupsApi{
tagGroups: services.TransactionTagGroups,
}
)
// TagGroupListHandler returns transaction tag group list of current user
func (a *TransactionTagGroupsApi) TagGroupListHandler(c *core.WebContext) (any, *errs.Error) {
uid := c.GetCurrentUid()
tagGroups, err := a.tagGroups.GetAllTagGroupsByUid(c, uid)
if err != nil {
log.Errorf(c, "[transaction_tag_groups.TagGroupListHandler] failed to get tag groups for user \"uid:%d\", because %s", uid, err.Error())
return nil, errs.Or(err, errs.ErrOperationFailed)
}
tagGroupResps := make(models.TransactionTagGroupInfoResponseSlice, len(tagGroups))
for i := 0; i < len(tagGroups); i++ {
tagGroupResps[i] = tagGroups[i].ToTransactionTagGroupInfoResponse()
}
sort.Sort(tagGroupResps)
return tagGroupResps, nil
}
// TagGroupGetHandler returns one specific transaction tag group of current user
func (a *TransactionTagGroupsApi) TagGroupGetHandler(c *core.WebContext) (any, *errs.Error) {
var tagGroupGetReq models.TransactionTagGroupGetRequest
err := c.ShouldBindQuery(&tagGroupGetReq)
if err != nil {
log.Warnf(c, "[transaction_tag_groups.TagGroupGetHandler] parse request failed, because %s", err.Error())
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
}
uid := c.GetCurrentUid()
tagGroup, err := a.tagGroups.GetTagGroupByTagGroupId(c, uid, tagGroupGetReq.Id)
if err != nil {
log.Errorf(c, "[transaction_tag_groups.TagGroupGetHandler] failed to get tag group \"id:%d\" for user \"uid:%d\", because %s", tagGroupGetReq.Id, uid, err.Error())
return nil, errs.Or(err, errs.ErrOperationFailed)
}
tagGroupResp := tagGroup.ToTransactionTagGroupInfoResponse()
return tagGroupResp, nil
}
// TagGroupCreateHandler saves a new transaction tag group by request parameters for current user
func (a *TransactionTagGroupsApi) TagGroupCreateHandler(c *core.WebContext) (any, *errs.Error) {
var tagGroupCreateReq models.TransactionTagGroupCreateRequest
err := c.ShouldBindJSON(&tagGroupCreateReq)
if err != nil {
log.Warnf(c, "[transaction_tag_groups.TagGroupCreateHandler] parse request failed, because %s", err.Error())
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
}
uid := c.GetCurrentUid()
maxOrderId, err := a.tagGroups.GetMaxDisplayOrder(c, uid)
if err != nil {
log.Errorf(c, "[transaction_tag_groups.TagGroupCreateHandler] failed to get max display order for user \"uid:%d\", because %s", uid, err.Error())
return nil, errs.Or(err, errs.ErrOperationFailed)
}
tagGroup := a.createNewTagGroupModel(uid, &tagGroupCreateReq, maxOrderId+1)
err = a.tagGroups.CreateTagGroup(c, tagGroup)
if err != nil {
log.Errorf(c, "[transaction_tag_groups.TagGroupCreateHandler] failed to create tag group \"id:%d\" for user \"uid:%d\", because %s", tagGroup.TagGroupId, uid, err.Error())
return nil, errs.Or(err, errs.ErrOperationFailed)
}
log.Infof(c, "[transaction_tag_groups.TagGroupCreateHandler] user \"uid:%d\" has created a new tag group \"id:%d\" successfully", uid, tagGroup.TagGroupId)
tagGroupResp := tagGroup.ToTransactionTagGroupInfoResponse()
return tagGroupResp, nil
}
// TagGroupModifyHandler saves an existed transaction tag group by request parameters for current user
func (a *TransactionTagGroupsApi) TagGroupModifyHandler(c *core.WebContext) (any, *errs.Error) {
var tagGroupModifyReq models.TransactionTagGroupModifyRequest
err := c.ShouldBindJSON(&tagGroupModifyReq)
if err != nil {
log.Warnf(c, "[transaction_tag_groups.TagGroupModifyHandler] parse request failed, because %s", err.Error())
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
}
uid := c.GetCurrentUid()
tagGroup, err := a.tagGroups.GetTagGroupByTagGroupId(c, uid, tagGroupModifyReq.Id)
if err != nil {
log.Errorf(c, "[transaction_tag_groups.TagGroupModifyHandler] failed to get tag group \"id:%d\" for user \"uid:%d\", because %s", tagGroupModifyReq.Id, uid, err.Error())
return nil, errs.Or(err, errs.ErrOperationFailed)
}
newTagGroup := &models.TransactionTagGroup{
TagGroupId: tagGroup.TagGroupId,
Uid: uid,
Name: tagGroupModifyReq.Name,
}
if newTagGroup.Name == tagGroup.Name {
return nil, errs.ErrNothingWillBeUpdated
}
err = a.tagGroups.ModifyTagGroup(c, newTagGroup)
if err != nil {
log.Errorf(c, "[transaction_tag_groups.TagGroupModifyHandler] failed to update tag group \"id:%d\" for user \"uid:%d\", because %s", tagGroupModifyReq.Id, uid, err.Error())
return nil, errs.Or(err, errs.ErrOperationFailed)
}
log.Infof(c, "[transaction_tag_groups.TagGroupModifyHandler] user \"uid:%d\" has updated tag group \"id:%d\" successfully", uid, tagGroupModifyReq.Id)
tagGroup.Name = newTagGroup.Name
tagGroupResp := tagGroup.ToTransactionTagGroupInfoResponse()
return tagGroupResp, nil
}
// TagGroupMoveHandler moves display order of existed transaction tag groups by request parameters for current user
func (a *TransactionTagGroupsApi) TagGroupMoveHandler(c *core.WebContext) (any, *errs.Error) {
var tagGroupMoveReq models.TransactionTagGroupMoveRequest
err := c.ShouldBindJSON(&tagGroupMoveReq)
if err != nil {
log.Warnf(c, "[transaction_tag_groups.TagGroupMoveHandler] parse request failed, because %s", err.Error())
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
}
uid := c.GetCurrentUid()
tagGroups := make([]*models.TransactionTagGroup, len(tagGroupMoveReq.NewDisplayOrders))
for i := 0; i < len(tagGroupMoveReq.NewDisplayOrders); i++ {
newDisplayOrder := tagGroupMoveReq.NewDisplayOrders[i]
tagGroup := &models.TransactionTagGroup{
Uid: uid,
TagGroupId: newDisplayOrder.Id,
DisplayOrder: newDisplayOrder.DisplayOrder,
}
tagGroups[i] = tagGroup
}
err = a.tagGroups.ModifyTagGroupDisplayOrders(c, uid, tagGroups)
if err != nil {
log.Errorf(c, "[transaction_tag_groups.TagGroupMoveHandler] failed to move tag groups for user \"uid:%d\", because %s", uid, err.Error())
return nil, errs.Or(err, errs.ErrOperationFailed)
}
log.Infof(c, "[transaction_tag_groups.TagGroupMoveHandler] user \"uid:%d\" has moved tag groups", uid)
return true, nil
}
// TagGroupDeleteHandler deletes an existed transaction tag group by request parameters for current user
func (a *TransactionTagGroupsApi) TagGroupDeleteHandler(c *core.WebContext) (any, *errs.Error) {
var tagGroupDeleteReq models.TransactionTagGroupDeleteRequest
err := c.ShouldBindJSON(&tagGroupDeleteReq)
if err != nil {
log.Warnf(c, "[transaction_tag_groups.TagGroupDeleteHandler] parse request failed, because %s", err.Error())
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
}
uid := c.GetCurrentUid()
err = a.tagGroups.DeleteTagGroup(c, uid, tagGroupDeleteReq.Id)
if err != nil {
log.Errorf(c, "[transaction_tag_groups.TagGroupDeleteHandler] failed to delete tag group \"id:%d\" for user \"uid:%d\", because %s", tagGroupDeleteReq.Id, uid, err.Error())
return nil, errs.Or(err, errs.ErrOperationFailed)
}
log.Infof(c, "[transaction_tag_groups.TagGroupDeleteHandler] user \"uid:%d\" has deleted tag group \"id:%d\"", uid, tagGroupDeleteReq.Id)
return true, nil
}
func (a *TransactionTagGroupsApi) createNewTagGroupModel(uid int64, tagGroupCreateReq *models.TransactionTagGroupCreateRequest, order int32) *models.TransactionTagGroup {
return &models.TransactionTagGroup{
Uid: uid,
Name: tagGroupCreateReq.Name,
DisplayOrder: order,
}
}
+33 -7
View File
@@ -78,7 +78,7 @@ func (a *TransactionTagsApi) TagCreateHandler(c *core.WebContext) (any, *errs.Er
uid := c.GetCurrentUid()
maxOrderId, err := a.tags.GetMaxDisplayOrder(c, uid)
maxOrderId, err := a.tags.GetMaxDisplayOrder(c, uid, tagCreateReq.GroupId)
if err != nil {
log.Errorf(c, "[transaction_tags.TagCreateHandler] failed to get max display order for user \"uid:%d\", because %s", uid, err.Error())
@@ -111,9 +111,16 @@ func (a *TransactionTagsApi) TagCreateBatchHandler(c *core.WebContext) (any, *er
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
}
for i := 0; i < len(tagCreateBatchReq.Tags); i++ {
if tagCreateBatchReq.Tags[i].GroupId != tagCreateBatchReq.GroupId {
log.Warnf(c, "[transaction_tags.TagCreateBatchHandler] the group id \"%d\" of tag#%d is inconsistent with the batch group id \"%d\"", tagCreateBatchReq.Tags[i].GroupId, i, tagCreateBatchReq.GroupId)
return nil, errs.ErrTransactionTagGroupIdInvalid
}
}
uid := c.GetCurrentUid()
maxOrderId, err := a.tags.GetMaxDisplayOrder(c, uid)
maxOrderId, err := a.tags.GetMaxDisplayOrder(c, uid, tagCreateBatchReq.GroupId)
if err != nil {
log.Errorf(c, "[transaction_tags.TagCreateBatchHandler] failed to get max display order for user \"uid:%d\", because %s", uid, err.Error())
@@ -161,16 +168,31 @@ func (a *TransactionTagsApi) TagModifyHandler(c *core.WebContext) (any, *errs.Er
}
newTag := &models.TransactionTag{
TagId: tag.TagId,
Uid: uid,
Name: tagModifyReq.Name,
TagId: tag.TagId,
Uid: uid,
Name: tagModifyReq.Name,
TagGroupId: tagModifyReq.GroupId,
DisplayOrder: tag.DisplayOrder,
}
if newTag.Name == tag.Name {
tagNameChanged := newTag.Name != tag.Name
if !tagNameChanged && newTag.TagGroupId == tag.TagGroupId {
return nil, errs.ErrNothingWillBeUpdated
}
err = a.tags.ModifyTag(c, newTag)
if newTag.TagGroupId != tag.TagGroupId {
maxOrderId, err := a.tags.GetMaxDisplayOrder(c, uid, newTag.TagGroupId)
if err != nil {
log.Errorf(c, "[transaction_tags.TagModifyHandler] failed to get max display order for user \"uid:%d\", because %s", uid, err.Error())
return nil, errs.Or(err, errs.ErrOperationFailed)
}
newTag.DisplayOrder = maxOrderId + 1
}
err = a.tags.ModifyTag(c, newTag, tagNameChanged)
if err != nil {
log.Errorf(c, "[transaction_tags.TagModifyHandler] failed to update tag \"id:%d\" for user \"uid:%d\", because %s", tagModifyReq.Id, uid, err.Error())
@@ -180,6 +202,8 @@ func (a *TransactionTagsApi) TagModifyHandler(c *core.WebContext) (any, *errs.Er
log.Infof(c, "[transaction_tags.TagModifyHandler] user \"uid:%d\" has updated tag \"id:%d\" successfully", uid, tagModifyReq.Id)
tag.Name = newTag.Name
tag.TagGroupId = newTag.TagGroupId
tag.DisplayOrder = newTag.DisplayOrder
tagResp := tag.ToTransactionTagInfoResponse()
return tagResp, nil
@@ -268,6 +292,7 @@ func (a *TransactionTagsApi) createNewTagModel(uid int64, tagCreateReq *models.T
return &models.TransactionTag{
Uid: uid,
Name: tagCreateReq.Name,
TagGroupId: tagCreateReq.GroupId,
DisplayOrder: order,
}
}
@@ -278,6 +303,7 @@ func (a *TransactionTagsApi) createNewTagModels(uid int64, tagCreateBatchReq *mo
for i := 0; i < len(tagCreateBatchReq.Tags); i++ {
tagCreateReq := tagCreateBatchReq.Tags[i]
tag := a.createNewTagModel(uid, tagCreateReq, order+int32(i))
tag.TagGroupId = tagCreateBatchReq.GroupId
tags[i] = tag
}
+1
View File
@@ -44,6 +44,7 @@ const (
NormalSubcategoryUserExternalAuth = 16
NormalSubcategoryOAuth2 = 17
NormalSubcategoryInsightsExplorer = 18
NormalSubcategoryTagGroup = 19
)
// Error represents the specific error returned to user
+10
View File
@@ -0,0 +1,10 @@
package errs
import "net/http"
// Error codes related to transaction tag groups
var (
ErrTransactionTagGroupIdInvalid = NewNormalError(NormalSubcategoryTagGroup, 0, http.StatusBadRequest, "transaction tag group id is invalid")
ErrTransactionTagGroupNotFound = NewNormalError(NormalSubcategoryTagGroup, 1, http.StatusBadRequest, "transaction tag group not found")
ErrTransactionTagGroupInUseCannotBeDeleted = NewNormalError(NormalSubcategoryTagGroup, 2, http.StatusBadRequest, "transaction tag group is in use and cannot be deleted")
)
+17 -6
View File
@@ -3,10 +3,11 @@ package models
// TransactionTag represents transaction tag data stored in database
type TransactionTag struct {
TagId int64 `xorm:"PK"`
Uid int64 `xorm:"INDEX(IDX_tag_uid_deleted_order) NOT NULL"`
Deleted bool `xorm:"INDEX(IDX_tag_uid_deleted_order) NOT NULL"`
Uid int64 `xorm:"INDEX(IDX_tag_uid_deleted_group_order) NOT NULL"`
Deleted bool `xorm:"INDEX(IDX_tag_uid_deleted_group_order) NOT NULL"`
TagGroupId int64 `xorm:"INDEX(IDX_tag_uid_deleted_group_order) NOT NULL"`
Name string `xorm:"VARCHAR(64) NOT NULL"`
DisplayOrder int32 `xorm:"INDEX(IDX_tag_uid_deleted_order) NOT NULL"`
DisplayOrder int32 `xorm:"INDEX(IDX_tag_uid_deleted_group_order) NOT NULL"`
Hidden bool `xorm:"NOT NULL"`
CreatedUnixTime int64
UpdatedUnixTime int64
@@ -20,19 +21,22 @@ type TransactionTagGetRequest struct {
// TransactionTagCreateRequest represents all parameters of transaction tag creation request
type TransactionTagCreateRequest struct {
Name string `json:"name" binding:"required,notBlank,max=64"`
GroupId int64 `json:"groupId,string"`
Name string `json:"name" binding:"required,notBlank,max=64"`
}
// TransactionTagCreateBatchRequest represents all parameters of transaction tag batch creation request
type TransactionTagCreateBatchRequest struct {
Tags []*TransactionTagCreateRequest `json:"tags" binding:"required"`
GroupId int64 `json:"groupId,string"`
SkipExists bool `json:"skipExists"`
}
// TransactionTagModifyRequest represents all parameters of transaction tag modification request
type TransactionTagModifyRequest struct {
Id int64 `json:"id,string" binding:"required,min=1"`
Name string `json:"name" binding:"required,notBlank,max=64"`
Id int64 `json:"id,string" binding:"required,min=1"`
GroupId int64 `json:"groupId,string"`
Name string `json:"name" binding:"required,notBlank,max=64"`
}
// TransactionTagHideRequest represents all parameters of transaction tag hiding request
@@ -61,6 +65,7 @@ type TransactionTagDeleteRequest struct {
type TransactionTagInfoResponse struct {
Id int64 `json:"id,string"`
Name string `json:"name"`
TagGroupId int64 `json:"groupId,string"`
DisplayOrder int32 `json:"displayOrder"`
Hidden bool `json:"hidden"`
}
@@ -71,6 +76,7 @@ func (t *TransactionTag) FillFromOtherTag(tag *TransactionTag) {
t.Uid = tag.Uid
t.Deleted = tag.Deleted
t.Name = tag.Name
t.TagGroupId = tag.TagGroupId
t.DisplayOrder = tag.DisplayOrder
t.Hidden = tag.Hidden
t.CreatedUnixTime = tag.CreatedUnixTime
@@ -83,6 +89,7 @@ func (t *TransactionTag) ToTransactionTagInfoResponse() *TransactionTagInfoRespo
return &TransactionTagInfoResponse{
Id: t.TagId,
Name: t.Name,
TagGroupId: t.TagGroupId,
DisplayOrder: t.DisplayOrder,
Hidden: t.Hidden,
}
@@ -103,5 +110,9 @@ func (s TransactionTagInfoResponseSlice) Swap(i, j int) {
// Less reports whether the first item is less than the second one
func (s TransactionTagInfoResponseSlice) Less(i, j int) bool {
if s[i].TagGroupId != s[j].TagGroupId {
return s[i].TagGroupId < s[j].TagGroupId
}
return s[i].DisplayOrder < s[j].DisplayOrder
}
+79
View File
@@ -0,0 +1,79 @@
package models
// TransactionTagGroup represents transaction tag group data stored in database
type TransactionTagGroup struct {
TagGroupId int64 `xorm:"PK"`
Uid int64 `xorm:"INDEX(IDX_tag_group_uid_deleted_order) NOT NULL"`
Deleted bool `xorm:"INDEX(IDX_tag_group_uid_deleted_order) NOT NULL"`
Name string `xorm:"VARCHAR(64) NOT NULL"`
DisplayOrder int32 `xorm:"INDEX(IDX_tag_group_uid_deleted_order) NOT NULL"`
CreatedUnixTime int64
UpdatedUnixTime int64
DeletedUnixTime int64
}
// TransactionTagGroupGetRequest represents all parameters of transaction tag group getting request
type TransactionTagGroupGetRequest struct {
Id int64 `form:"id,string" binding:"required,min=1"`
}
// TransactionTagGroupCreateRequest represents all parameters of transaction tag group creation request
type TransactionTagGroupCreateRequest struct {
Name string `json:"name" binding:"required,notBlank,max=64"`
}
// TransactionTagGroupModifyRequest represents all parameters of transaction tag group modification request
type TransactionTagGroupModifyRequest struct {
Id int64 `json:"id,string" binding:"required,min=1"`
Name string `json:"name" binding:"required,notBlank,max=64"`
}
// TransactionTagGroupMoveRequest represents all parameters of transaction tag group moving request
type TransactionTagGroupMoveRequest struct {
NewDisplayOrders []*TransactionTagGroupNewDisplayOrderRequest `json:"newDisplayOrders" binding:"required,min=1"`
}
// TransactionTagGroupNewDisplayOrderRequest represents a data pair of id and display order
type TransactionTagGroupNewDisplayOrderRequest struct {
Id int64 `json:"id,string" binding:"required,min=1"`
DisplayOrder int32 `json:"displayOrder"`
}
// TransactionTagGroupDeleteRequest represents all parameters of transaction tag group deleting request
type TransactionTagGroupDeleteRequest struct {
Id int64 `json:"id,string" binding:"required,min=1"`
}
// TransactionTagGroupInfoResponse represents a view-object of transaction tag group
type TransactionTagGroupInfoResponse struct {
Id int64 `json:"id,string"`
Name string `json:"name"`
DisplayOrder int32 `json:"displayOrder"`
}
// ToTransactionTagGroupInfoResponse returns a view-object according to database model
func (t *TransactionTagGroup) ToTransactionTagGroupInfoResponse() *TransactionTagGroupInfoResponse {
return &TransactionTagGroupInfoResponse{
Id: t.TagGroupId,
Name: t.Name,
DisplayOrder: t.DisplayOrder,
}
}
// TransactionTagGroupInfoResponseSlice represents the slice data structure of TransactionTagGroupInfoResponse
type TransactionTagGroupInfoResponseSlice []*TransactionTagGroupInfoResponse
// Len returns the count of items
func (s TransactionTagGroupInfoResponseSlice) Len() int {
return len(s)
}
// Swap swaps two items
func (s TransactionTagGroupInfoResponseSlice) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
// Less reports whether the first item is less than the second one
func (s TransactionTagGroupInfoResponseSlice) Less(i, j int) bool {
return s[i].DisplayOrder < s[j].DisplayOrder
}
+30
View File
@@ -0,0 +1,30 @@
package models
import (
"sort"
"testing"
"github.com/stretchr/testify/assert"
)
func TestTransactionTagGroupInfoResponseSliceLess(t *testing.T) {
var transactionTagGroupRespSlice TransactionTagGroupInfoResponseSlice
transactionTagGroupRespSlice = append(transactionTagGroupRespSlice, &TransactionTagGroupInfoResponse{
Id: 1,
DisplayOrder: 3,
})
transactionTagGroupRespSlice = append(transactionTagGroupRespSlice, &TransactionTagGroupInfoResponse{
Id: 2,
DisplayOrder: 1,
})
transactionTagGroupRespSlice = append(transactionTagGroupRespSlice, &TransactionTagGroupInfoResponse{
Id: 3,
DisplayOrder: 2,
})
sort.Sort(transactionTagGroupRespSlice)
assert.Equal(t, int64(2), transactionTagGroupRespSlice[0].Id)
assert.Equal(t, int64(3), transactionTagGroupRespSlice[1].Id)
assert.Equal(t, int64(1), transactionTagGroupRespSlice[2].Id)
}
+24 -3
View File
@@ -11,20 +11,41 @@ func TestTransactionTagInfoResponseSliceLess(t *testing.T) {
var transactionTagRespSlice TransactionTagInfoResponseSlice
transactionTagRespSlice = append(transactionTagRespSlice, &TransactionTagInfoResponse{
Id: 1,
TagGroupId: 0,
DisplayOrder: 3,
})
transactionTagRespSlice = append(transactionTagRespSlice, &TransactionTagInfoResponse{
Id: 2,
DisplayOrder: 1,
TagGroupId: 1,
DisplayOrder: 2,
})
transactionTagRespSlice = append(transactionTagRespSlice, &TransactionTagInfoResponse{
Id: 3,
TagGroupId: 0,
DisplayOrder: 1,
})
transactionTagRespSlice = append(transactionTagRespSlice, &TransactionTagInfoResponse{
Id: 4,
TagGroupId: 2,
DisplayOrder: 1,
})
transactionTagRespSlice = append(transactionTagRespSlice, &TransactionTagInfoResponse{
Id: 5,
TagGroupId: 1,
DisplayOrder: 1,
})
transactionTagRespSlice = append(transactionTagRespSlice, &TransactionTagInfoResponse{
Id: 6,
TagGroupId: 0,
DisplayOrder: 2,
})
sort.Sort(transactionTagRespSlice)
assert.Equal(t, int64(2), transactionTagRespSlice[0].Id)
assert.Equal(t, int64(3), transactionTagRespSlice[1].Id)
assert.Equal(t, int64(3), transactionTagRespSlice[0].Id)
assert.Equal(t, int64(6), transactionTagRespSlice[1].Id)
assert.Equal(t, int64(1), transactionTagRespSlice[2].Id)
assert.Equal(t, int64(5), transactionTagRespSlice[3].Id)
assert.Equal(t, int64(2), transactionTagRespSlice[4].Id)
assert.Equal(t, int64(4), transactionTagRespSlice[5].Id)
}
+220
View File
@@ -0,0 +1,220 @@
package services
import (
"time"
"xorm.io/xorm"
"github.com/mayswind/ezbookkeeping/pkg/core"
"github.com/mayswind/ezbookkeeping/pkg/datastore"
"github.com/mayswind/ezbookkeeping/pkg/errs"
"github.com/mayswind/ezbookkeeping/pkg/models"
"github.com/mayswind/ezbookkeeping/pkg/uuid"
)
// TransactionTagGroupService represents transaction tag group service
type TransactionTagGroupService struct {
ServiceUsingDB
ServiceUsingUuid
}
// Initialize a transaction tag group service singleton instance
var (
TransactionTagGroups = &TransactionTagGroupService{
ServiceUsingDB: ServiceUsingDB{
container: datastore.Container,
},
ServiceUsingUuid: ServiceUsingUuid{
container: uuid.Container,
},
}
)
// GetAllTagGroupsByUid returns all transaction tag group models of user
func (s *TransactionTagGroupService) GetAllTagGroupsByUid(c core.Context, uid int64) ([]*models.TransactionTagGroup, error) {
if uid <= 0 {
return nil, errs.ErrUserIdInvalid
}
var tagGroups []*models.TransactionTagGroup
err := s.UserDataDB(uid).NewSession(c).Where("uid=? AND deleted=?", uid, false).Find(&tagGroups)
return tagGroups, err
}
// GetTagGroupByTagGroupId returns a transaction tag group model according to transaction tag group id
func (s *TransactionTagGroupService) GetTagGroupByTagGroupId(c core.Context, uid int64, tagGroupId int64) (*models.TransactionTagGroup, error) {
if uid <= 0 {
return nil, errs.ErrUserIdInvalid
}
if tagGroupId <= 0 {
return nil, errs.ErrTransactionTagGroupIdInvalid
}
tagGroup := &models.TransactionTagGroup{}
has, err := s.UserDataDB(uid).NewSession(c).ID(tagGroupId).Where("uid=? AND deleted=?", uid, false).Get(tagGroup)
if err != nil {
return nil, err
} else if !has {
return nil, errs.ErrTransactionTagGroupNotFound
}
return tagGroup, nil
}
// GetMaxDisplayOrder returns the max display order
func (s *TransactionTagGroupService) GetMaxDisplayOrder(c core.Context, uid int64) (int32, error) {
if uid <= 0 {
return 0, errs.ErrUserIdInvalid
}
tagGroup := &models.TransactionTagGroup{}
has, err := s.UserDataDB(uid).NewSession(c).Cols("uid", "deleted", "display_order").Where("uid=? AND deleted=?", uid, false).OrderBy("display_order desc").Limit(1).Get(tagGroup)
if err != nil {
return 0, err
}
if has {
return tagGroup.DisplayOrder, nil
} else {
return 0, nil
}
}
// CreateTagGroup saves a new transaction tag group model to database
func (s *TransactionTagGroupService) CreateTagGroup(c core.Context, tagGroup *models.TransactionTagGroup) error {
if tagGroup.Uid <= 0 {
return errs.ErrUserIdInvalid
}
tagGroup.TagGroupId = s.GenerateUuid(uuid.UUID_TYPE_TAG_GROUP)
if tagGroup.TagGroupId < 1 {
return errs.ErrSystemIsBusy
}
tagGroup.Deleted = false
tagGroup.CreatedUnixTime = time.Now().Unix()
tagGroup.UpdatedUnixTime = time.Now().Unix()
return s.UserDataDB(tagGroup.Uid).DoTransaction(c, func(sess *xorm.Session) error {
_, err := sess.Insert(tagGroup)
return err
})
}
// ModifyTagGroup saves an existed transaction tag group model to database
func (s *TransactionTagGroupService) ModifyTagGroup(c core.Context, tagGroup *models.TransactionTagGroup) error {
if tagGroup.Uid <= 0 {
return errs.ErrUserIdInvalid
}
tagGroup.UpdatedUnixTime = time.Now().Unix()
return s.UserDataDB(tagGroup.Uid).DoTransaction(c, func(sess *xorm.Session) error {
updatedRows, err := sess.ID(tagGroup.TagGroupId).Cols("name", "updated_unix_time").Where("uid=? AND deleted=?", tagGroup.Uid, false).Update(tagGroup)
if err != nil {
return err
} else if updatedRows < 1 {
return errs.ErrTransactionTagGroupNotFound
}
return err
})
}
// ModifyTagGroupDisplayOrders updates display order of given transaction tag groups
func (s *TransactionTagGroupService) ModifyTagGroupDisplayOrders(c core.Context, uid int64, tagGroups []*models.TransactionTagGroup) error {
if uid <= 0 {
return errs.ErrUserIdInvalid
}
for i := 0; i < len(tagGroups); i++ {
tagGroups[i].UpdatedUnixTime = time.Now().Unix()
}
return s.UserDataDB(uid).DoTransaction(c, func(sess *xorm.Session) error {
for i := 0; i < len(tagGroups); i++ {
tagGroup := tagGroups[i]
updatedRows, err := sess.ID(tagGroup.TagGroupId).Cols("display_order", "updated_unix_time").Where("uid=? AND deleted=?", uid, false).Update(tagGroup)
if err != nil {
return err
} else if updatedRows < 1 {
return errs.ErrTransactionTagGroupNotFound
}
}
return nil
})
}
// DeleteTagGroup deletes an existed transaction tag group from database
func (s *TransactionTagGroupService) DeleteTagGroup(c core.Context, uid int64, tagGroupId int64) error {
if uid <= 0 {
return errs.ErrUserIdInvalid
}
now := time.Now().Unix()
updateModel := &models.TransactionTagGroup{
Deleted: true,
DeletedUnixTime: now,
}
return s.UserDataDB(uid).DoTransaction(c, func(sess *xorm.Session) error {
exists, err := sess.Cols("uid", "deleted").Where("uid=? AND deleted=? AND tag_group_id=?", uid, false, tagGroupId).Limit(1).Exist(&models.TransactionTag{})
if err != nil {
return err
} else if exists {
return errs.ErrTransactionTagGroupInUseCannotBeDeleted
}
deletedRows, err := sess.ID(tagGroupId).Cols("deleted", "deleted_unix_time").Where("uid=? AND deleted=?", uid, false).Update(updateModel)
if err != nil {
return err
} else if deletedRows < 1 {
return errs.ErrTransactionTagGroupNotFound
}
return err
})
}
// DeleteAllTagGroups deletes all existed transaction tag groups from database
func (s *TransactionTagGroupService) DeleteAllTagGroups(c core.Context, uid int64) error {
if uid <= 0 {
return errs.ErrUserIdInvalid
}
now := time.Now().Unix()
updateModel := &models.TransactionTagGroup{
Deleted: true,
DeletedUnixTime: now,
}
return s.UserDataDB(uid).DoTransaction(c, func(sess *xorm.Session) error {
exists, err := sess.Cols("uid", "deleted").Where("uid=? AND deleted=? AND tag_group_id>?", uid, false, 0).Limit(1).Exist(&models.TransactionTag{})
if err != nil {
return err
} else if exists {
return errs.ErrTransactionTagGroupInUseCannotBeDeleted
}
_, err = sess.Cols("deleted", "deleted_unix_time").Where("uid=? AND deleted=?", uid, false).Update(updateModel)
if err != nil {
return err
}
return nil
})
}
+11 -9
View File
@@ -101,13 +101,13 @@ func (s *TransactionTagService) GetTagsByTagIds(c core.Context, uid int64, tagId
}
// GetMaxDisplayOrder returns the max display order
func (s *TransactionTagService) GetMaxDisplayOrder(c core.Context, uid int64) (int32, error) {
func (s *TransactionTagService) GetMaxDisplayOrder(c core.Context, uid int64, tagGroupId int64) (int32, error) {
if uid <= 0 {
return 0, errs.ErrUserIdInvalid
}
tag := &models.TransactionTag{}
has, err := s.UserDataDB(uid).NewSession(c).Cols("uid", "deleted", "display_order").Where("uid=? AND deleted=?", uid, false).OrderBy("display_order desc").Limit(1).Get(tag)
has, err := s.UserDataDB(uid).NewSession(c).Cols("uid", "deleted", "display_order").Where("uid=? AND deleted=? AND tag_group_id=?", uid, false, tagGroupId).OrderBy("display_order desc").Limit(1).Get(tag)
if err != nil {
return 0, err
@@ -294,23 +294,25 @@ func (s *TransactionTagService) CreateTags(c core.Context, uid int64, tags []*mo
}
// ModifyTag saves an existed transaction tag model to database
func (s *TransactionTagService) ModifyTag(c core.Context, tag *models.TransactionTag) error {
func (s *TransactionTagService) ModifyTag(c core.Context, tag *models.TransactionTag, tagNameChanged bool) error {
if tag.Uid <= 0 {
return errs.ErrUserIdInvalid
}
exists, err := s.ExistsTagName(c, tag.Uid, tag.Name)
if tagNameChanged {
exists, err := s.ExistsTagName(c, tag.Uid, tag.Name)
if err != nil {
return err
} else if exists {
return errs.ErrTransactionTagNameAlreadyExists
if err != nil {
return err
} else if exists {
return errs.ErrTransactionTagNameAlreadyExists
}
}
tag.UpdatedUnixTime = time.Now().Unix()
return s.UserDataDB(tag.Uid).DoTransaction(c, func(sess *xorm.Session) error {
updatedRows, err := sess.ID(tag.TagId).Cols("name", "updated_unix_time").Where("uid=? AND deleted=?", tag.Uid, false).Update(tag)
updatedRows, err := sess.ID(tag.TagId).Cols("name", "tag_group_id", "display_order", "updated_unix_time").Where("uid=? AND deleted=?", tag.Uid, false).Update(tag)
if err != nil {
return err
+1
View File
@@ -15,4 +15,5 @@ const (
UUID_TYPE_TEMPLATE UuidType = 7
UUID_TYPE_PICTURE UuidType = 8
UUID_TYPE_EXPLORER UuidType = 9
UUID_TYPE_TAG_GROUP UuidType = 10
)