support scheduled transaction (#2)
This commit is contained in:
+11
-1
@@ -26,7 +26,7 @@ func (s *ServiceUsingDB) TokenDB(uid int64) *datastore.Database {
|
||||
return s.container.TokenStore.Choose(uid)
|
||||
}
|
||||
|
||||
// TokenDBByIndex returns the datastore by index
|
||||
// TokenDBByIndex returns the datastore which contains user token by index
|
||||
func (s *ServiceUsingDB) TokenDBByIndex(index int) *datastore.Database {
|
||||
return s.container.TokenStore.Get(index)
|
||||
}
|
||||
@@ -41,6 +41,16 @@ func (s *ServiceUsingDB) UserDataDB(uid int64) *datastore.Database {
|
||||
return s.container.UserDataStore.Choose(uid)
|
||||
}
|
||||
|
||||
// UserDataDBByIndex returns the datastore which contains user data by index
|
||||
func (s *ServiceUsingDB) UserDataDBByIndex(index int) *datastore.Database {
|
||||
return s.container.UserDataStore.Get(index)
|
||||
}
|
||||
|
||||
// UserDataDBCount returns the count of datastores which contains user data
|
||||
func (s *ServiceUsingDB) UserDataDBCount() int {
|
||||
return s.container.UserDataStore.Count()
|
||||
}
|
||||
|
||||
// ServiceUsingConfig represents a service that need to use config
|
||||
type ServiceUsingConfig struct {
|
||||
container *settings.ConfigContainer
|
||||
|
||||
@@ -29,7 +29,7 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
// GetTotalNormalTemplateCountByUid returns total template count of user
|
||||
// GetTotalNormalTemplateCountByUid returns total normal template count of user
|
||||
func (s *TransactionTemplateService) GetTotalNormalTemplateCountByUid(c core.Context, uid int64) (int64, error) {
|
||||
if uid <= 0 {
|
||||
return 0, errs.ErrUserIdInvalid
|
||||
@@ -40,6 +40,17 @@ func (s *TransactionTemplateService) GetTotalNormalTemplateCountByUid(c core.Con
|
||||
return count, err
|
||||
}
|
||||
|
||||
// GetTotalScheduledTemplateCountByUid returns total scheduled transaction count of user
|
||||
func (s *TransactionTemplateService) GetTotalScheduledTemplateCountByUid(c core.Context, uid int64) (int64, error) {
|
||||
if uid <= 0 {
|
||||
return 0, errs.ErrUserIdInvalid
|
||||
}
|
||||
|
||||
count, err := s.UserDataDB(uid).NewSession(c).Where("uid=? AND deleted=? AND template_type=?", uid, false, models.TRANSACTION_TEMPLATE_TYPE_SCHEDULE).Count(&models.TransactionTemplate{})
|
||||
|
||||
return count, err
|
||||
}
|
||||
|
||||
// GetAllTemplatesByUid returns all transaction template models of user
|
||||
func (s *TransactionTemplateService) GetAllTemplatesByUid(c core.Context, uid int64, templateType models.TransactionTemplateType) ([]*models.TransactionTemplate, error) {
|
||||
if uid <= 0 {
|
||||
@@ -125,7 +136,7 @@ func (s *TransactionTemplateService) ModifyTemplate(c core.Context, template *mo
|
||||
template.UpdatedUnixTime = time.Now().Unix()
|
||||
|
||||
return s.UserDataDB(template.Uid).DoTransaction(c, func(sess *xorm.Session) error {
|
||||
updatedRows, err := sess.ID(template.TemplateId).Cols("name", "type", "category_id", "account_id", "tag_ids", "amount", "related_account_id", "related_account_amount", "hide_amount", "comment", "updated_unix_time").Where("uid=? AND deleted=?", template.Uid, false).Update(template)
|
||||
updatedRows, err := sess.ID(template.TemplateId).Cols("name", "type", "category_id", "account_id", "scheduled_frequency_type", "scheduled_frequency", "scheduled_at", "scheduled_timezone_utc_offset", "tag_ids", "amount", "related_account_id", "related_account_amount", "hide_amount", "comment", "updated_unix_time").Where("uid=? AND deleted=?", template.Uid, false).Update(template)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/datastore"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/log"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/utils"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/uuid"
|
||||
@@ -429,6 +430,135 @@ func (s *TransactionService) CreateTransaction(c core.Context, transaction *mode
|
||||
})
|
||||
}
|
||||
|
||||
// CreateScheduledTransactions saves all scheduled transactions that should be created now
|
||||
func (s *TransactionService) CreateScheduledTransactions(c core.Context, currentUnixTime int64, interval time.Duration) error {
|
||||
var allTemplates []*models.TransactionTemplate
|
||||
intervalMinute := int(interval / time.Minute)
|
||||
currentTime := time.Unix(currentUnixTime, 0)
|
||||
currentMinute := (currentTime.Minute() / intervalMinute) * intervalMinute
|
||||
|
||||
startTime := time.Date(currentTime.Year(), currentTime.Month(), currentTime.Day(), currentTime.Hour(), currentMinute, 0, 0, time.Local)
|
||||
startTimeInUTC := startTime.In(time.UTC)
|
||||
|
||||
minutesElapsedOfDayInUtc := startTimeInUTC.Hour()*60 + startTimeInUTC.Minute()
|
||||
secondsElapsedOfDayInUtc := minutesElapsedOfDayInUtc * 60
|
||||
todayFirstTimeInUTC := startTimeInUTC.Add(time.Duration(-secondsElapsedOfDayInUtc) * time.Second)
|
||||
todayFirstUnixTimeInUTC := todayFirstTimeInUTC.Unix()
|
||||
|
||||
minScheduledAt := minutesElapsedOfDayInUtc
|
||||
maxScheduledAt := minScheduledAt + intervalMinute
|
||||
|
||||
for i := 0; i < s.UserDataDBCount(); i++ {
|
||||
var templates []*models.TransactionTemplate
|
||||
err := s.UserDataDBByIndex(i).NewSession(c).Where("deleted=? AND template_type=? AND (scheduled_frequency_type=? OR scheduled_frequency_type=?) AND scheduled_at>=? AND scheduled_at<?", false, models.TRANSACTION_TEMPLATE_TYPE_SCHEDULE, models.TRANSACTION_SCHEDULE_FREQUENCY_TYPE_WEEKLY, models.TRANSACTION_SCHEDULE_FREQUENCY_TYPE_MONTHLY, minScheduledAt, maxScheduledAt).Find(&templates)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
allTemplates = append(allTemplates, templates...)
|
||||
}
|
||||
|
||||
if len(allTemplates) < 1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Infof(c, "[transactions.CreateScheduledTransactions] should process %d scheduled transaction templates now (scheduled at from %d to %d)", len(allTemplates), minScheduledAt, maxScheduledAt)
|
||||
|
||||
successCount := 0
|
||||
skipCount := 0
|
||||
failedCount := 0
|
||||
|
||||
for i := 0; i < len(allTemplates); i++ {
|
||||
template := allTemplates[i]
|
||||
|
||||
if template.ScheduledFrequencyType == models.TRANSACTION_SCHEDULE_FREQUENCY_TYPE_DISABLED {
|
||||
skipCount++
|
||||
log.Warnf(c, "[transactions.CreateScheduledTransactions] transaction template \"id:%d\" disabled scheduled transaction frequency", template.TemplateId)
|
||||
continue
|
||||
}
|
||||
|
||||
if (template.ScheduledFrequencyType != models.TRANSACTION_SCHEDULE_FREQUENCY_TYPE_WEEKLY &&
|
||||
template.ScheduledFrequencyType != models.TRANSACTION_SCHEDULE_FREQUENCY_TYPE_MONTHLY) ||
|
||||
template.ScheduledFrequency == "" {
|
||||
skipCount++
|
||||
log.Warnf(c, "[transactions.CreateScheduledTransactions] transaction template \"id:%d\" has invalid scheduled transaction frequency", template.TemplateId)
|
||||
continue
|
||||
}
|
||||
|
||||
frequencyValues, err := utils.StringArrayToInt64Array(strings.Split(template.ScheduledFrequency, ","))
|
||||
|
||||
if err != nil {
|
||||
skipCount++
|
||||
log.Warnf(c, "[transactions.CreateScheduledTransactions] transaction template \"id:%d\" has invalid scheduled transaction frequency, because %s", template.TemplateId, err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
frequencyValueSet := utils.ToSet(frequencyValues)
|
||||
templateTimeZone := time.FixedZone("Template Timezone", int(template.ScheduledTimezoneUtcOffset)*60)
|
||||
transactionUnixTime := todayFirstUnixTimeInUTC + int64(template.ScheduledAt)*60
|
||||
transactionTime := time.Unix(transactionUnixTime, 0).In(templateTimeZone)
|
||||
|
||||
if template.ScheduledFrequencyType == models.TRANSACTION_SCHEDULE_FREQUENCY_TYPE_WEEKLY && !frequencyValueSet[int64(transactionTime.Weekday())] {
|
||||
skipCount++
|
||||
log.Infof(c, "[transactions.CreateScheduledTransactions] transaction template \"id:%d\" does not need to create transaction, today is %s", template.TemplateId, startTimeInUTC.Weekday())
|
||||
continue
|
||||
} else if template.ScheduledFrequencyType == models.TRANSACTION_SCHEDULE_FREQUENCY_TYPE_MONTHLY && !frequencyValueSet[int64(transactionTime.Day())] {
|
||||
skipCount++
|
||||
log.Infof(c, "[transactions.CreateScheduledTransactions] transaction template \"id:%d\" does not need to create transaction, today is %d of month", template.TemplateId, startTimeInUTC.Day())
|
||||
continue
|
||||
}
|
||||
|
||||
var transactionDbType models.TransactionDbType
|
||||
|
||||
if template.Type == models.TRANSACTION_TYPE_EXPENSE {
|
||||
transactionDbType = models.TRANSACTION_DB_TYPE_EXPENSE
|
||||
} else if template.Type == models.TRANSACTION_TYPE_INCOME {
|
||||
transactionDbType = models.TRANSACTION_DB_TYPE_INCOME
|
||||
} else if template.Type == models.TRANSACTION_TYPE_TRANSFER {
|
||||
transactionDbType = models.TRANSACTION_DB_TYPE_TRANSFER_OUT
|
||||
} else {
|
||||
skipCount++
|
||||
log.Warnf(c, "[transactions.CreateScheduledTransactions] transaction template \"id:%d\" has invalid transaction type", template.TemplateId)
|
||||
continue
|
||||
}
|
||||
|
||||
transaction := &models.Transaction{
|
||||
Uid: template.Uid,
|
||||
Type: transactionDbType,
|
||||
CategoryId: template.CategoryId,
|
||||
TransactionTime: utils.GetMinTransactionTimeFromUnixTime(transactionTime.Unix()),
|
||||
TimezoneUtcOffset: template.ScheduledTimezoneUtcOffset,
|
||||
AccountId: template.AccountId,
|
||||
Amount: template.Amount,
|
||||
HideAmount: template.HideAmount,
|
||||
Comment: template.Comment,
|
||||
CreatedIp: "127.0.0.1",
|
||||
ScheduledCreated: true,
|
||||
}
|
||||
|
||||
if template.Type == models.TRANSACTION_TYPE_TRANSFER {
|
||||
transaction.RelatedAccountId = template.RelatedAccountId
|
||||
transaction.RelatedAccountAmount = template.RelatedAccountAmount
|
||||
}
|
||||
|
||||
tagIds := template.GetTagIds()
|
||||
err = s.CreateTransaction(c, transaction, tagIds)
|
||||
|
||||
if err == nil {
|
||||
successCount++
|
||||
log.Infof(c, "[transactions.CreateScheduledTransactions] transaction template \"id:%d\" has created a new trasaction \"id:%d\"", template.TemplateId, transaction.TransactionId)
|
||||
} else {
|
||||
failedCount++
|
||||
log.Errorf(c, "[transactions.CreateScheduledTransactions] transaction template \"id:%d\" failed to create new trasaction", template.TemplateId)
|
||||
}
|
||||
}
|
||||
|
||||
log.Infof(c, "[transactions.CreateScheduledTransactions] %d transactions has been created successfully, %d templates does not need to create transactions and %d transactions failed to create", successCount, skipCount, failedCount)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ModifyTransaction saves an existed transaction to database
|
||||
func (s *TransactionService) ModifyTransaction(c core.Context, transaction *models.Transaction, currentTagIdsCount int, addTagIds []int64, removeTagIds []int64) error {
|
||||
if transaction.Uid <= 0 {
|
||||
|
||||
Reference in New Issue
Block a user