From bfb56fad48be0a842764e5b4d7e4c72d30f1f7a5 Mon Sep 17 00:00:00 2001 From: MaysWind Date: Sat, 13 Mar 2021 21:02:02 +0800 Subject: [PATCH] move query parameter utc_offset to header --- pkg/api/transactions.go | 36 ++++++++++++++++++++++++++++++++---- pkg/core/context.go | 14 ++++++++++++++ pkg/errs/global.go | 1 + pkg/models/transaction.go | 10 +++------- pkg/models/user.go | 4 ++-- src/lib/services.js | 12 +++++------- src/locales/en.js | 1 + src/locales/zh_Hans.js | 1 + 8 files changed, 59 insertions(+), 20 deletions(-) diff --git a/pkg/api/transactions.go b/pkg/api/transactions.go index f56518dd..c78be752 100644 --- a/pkg/api/transactions.go +++ b/pkg/api/transactions.go @@ -41,6 +41,13 @@ func (a *TransactionsApi) TransactionListHandler(c *core.Context) (interface{}, return nil, errs.NewIncompleteOrIncorrectSubmissionError(err) } + utcOffset, err := c.GetClientTimezoneOffset() + + if err != nil { + log.WarnfWithRequestId(c, "[transactions.TransactionListHandler] cannot get client timezone offset, because %s", err.Error()) + return nil, errs.ErrClientTimezoneOffsetInvalid + } + uid := c.GetCurrentUid() user, err := a.users.GetUserById(uid) @@ -131,7 +138,7 @@ func (a *TransactionsApi) TransactionListHandler(c *core.Context) (interface{}, transaction = a.transactions.GetRelatedTransferTransaction(transaction, transaction.RelatedId) } - transactionEditable := transaction.IsEditable(user, transactionListReq.UtcOffset, allAccounts[transaction.AccountId], allAccounts[transaction.RelatedAccountId]) + transactionEditable := transaction.IsEditable(user, utcOffset, allAccounts[transaction.AccountId], allAccounts[transaction.RelatedAccountId]) transactionTagIds := allTransactionTagIds[transaction.TransactionId] transactionResps.Items[i] = transaction.ToTransactionInfoResponse(transactionTagIds, transactionEditable) } @@ -155,6 +162,13 @@ func (a *TransactionsApi) TransactionMonthListHandler(c *core.Context) (interfac return nil, errs.NewIncompleteOrIncorrectSubmissionError(err) } + utcOffset, err := c.GetClientTimezoneOffset() + + if err != nil { + log.WarnfWithRequestId(c, "[transactions.TransactionMonthListHandler] cannot get client timezone offset, because %s", err.Error()) + return nil, errs.ErrClientTimezoneOffsetInvalid + } + uid := c.GetCurrentUid() user, err := a.users.GetUserById(uid) @@ -235,7 +249,7 @@ func (a *TransactionsApi) TransactionMonthListHandler(c *core.Context) (interfac transaction = a.transactions.GetRelatedTransferTransaction(transaction, transaction.RelatedId) } - transactionEditable := transaction.IsEditable(user, transactionListReq.UtcOffset, allAccounts[transaction.AccountId], allAccounts[transaction.RelatedAccountId]) + transactionEditable := transaction.IsEditable(user, utcOffset, allAccounts[transaction.AccountId], allAccounts[transaction.RelatedAccountId]) transactionTagIds := allTransactionTagIds[transaction.TransactionId] transactionResps[i] = transaction.ToTransactionInfoResponse(transactionTagIds, transactionEditable) } @@ -253,6 +267,13 @@ func (a *TransactionsApi) TransactionGetHandler(c *core.Context) (interface{}, * return nil, errs.NewIncompleteOrIncorrectSubmissionError(err) } + utcOffset, err := c.GetClientTimezoneOffset() + + if err != nil { + log.WarnfWithRequestId(c, "[transactions.TransactionGetHandler] cannot get client timezone offset, because %s", err.Error()) + return nil, errs.ErrClientTimezoneOffsetInvalid + } + uid := c.GetCurrentUid() user, err := a.users.GetUserById(uid) @@ -304,7 +325,7 @@ func (a *TransactionsApi) TransactionGetHandler(c *core.Context) (interface{}, * return nil, errs.ErrOperationFailed } - transactionEditable := transaction.IsEditable(user, transactionGetReq.UtcOffset, accountMap[transaction.AccountId], accountMap[transaction.RelatedAccountId]) + transactionEditable := transaction.IsEditable(user, utcOffset, accountMap[transaction.AccountId], accountMap[transaction.RelatedAccountId]) transactionTagIds := allTransactionTagIds[transaction.TransactionId] transactionResp := transaction.ToTransactionInfoResponse(transactionTagIds, transactionEditable) @@ -497,6 +518,13 @@ func (a *TransactionsApi) TransactionDeleteHandler(c *core.Context) (interface{} return nil, errs.NewIncompleteOrIncorrectSubmissionError(err) } + utcOffset, err := c.GetClientTimezoneOffset() + + if err != nil { + log.WarnfWithRequestId(c, "[transactions.TransactionDeleteHandler] cannot get client timezone offset, because %s", err.Error()) + return nil, errs.ErrClientTimezoneOffsetInvalid + } + uid := c.GetCurrentUid() user, err := a.users.GetUserById(uid) @@ -520,7 +548,7 @@ func (a *TransactionsApi) TransactionDeleteHandler(c *core.Context) (interface{} return nil, errs.ErrTransactionTypeInvalid } - transactionEditable := user.CanEditTransactionByTransactionTime(transaction.TransactionTime, transactionDeleteReq.UtcOffset) + transactionEditable := user.CanEditTransactionByTransactionTime(transaction.TransactionTime, utcOffset) if !transactionEditable { return nil, errs.ErrCannotDeleteTransactionWithThisTransactionTime diff --git a/pkg/core/context.go b/pkg/core/context.go index da6b240a..053f050d 100644 --- a/pkg/core/context.go +++ b/pkg/core/context.go @@ -12,6 +12,8 @@ const requestIdFieldKey = "REQUEST_ID" const tokenClaimsFieldKey = "TOKEN_CLAIMS" const responseErrorFieldKey = "RESPONSE_ERROR" +const clientTimezoneOffsetHeaderName = "X-Timezone-Offset" + // Context represents the request and response context type Context struct { *gin.Context @@ -67,6 +69,18 @@ func (c *Context) GetCurrentUid() int64 { return uid } +// GetClientTimezoneOffset returns the client timezone offset +func (c *Context) GetClientTimezoneOffset() (int16, error) { + value := c.GetHeader(clientTimezoneOffsetHeaderName) + offset, err := strconv.Atoi(value) + + if err != nil { + return 0, err + } + + return int16(offset), nil +} + // SetResponseError sets the response error func (c *Context) SetResponseError(error *errs.Error) { c.Set(responseErrorFieldKey, error) diff --git a/pkg/errs/global.go b/pkg/errs/global.go index 61acce86..d4ba9d4e 100644 --- a/pkg/errs/global.go +++ b/pkg/errs/global.go @@ -15,6 +15,7 @@ var ( ErrFailedToRequestRemoteApi = NewNormalError(NormalSubcategoryGlobal, 5, http.StatusBadRequest, "failed to request third party api") ErrPageIndexInvalid = NewNormalError(NormalSubcategoryGlobal, 6, http.StatusBadRequest, "page index is invalid") ErrPageCountInvalid = NewNormalError(NormalSubcategoryGlobal, 7, http.StatusBadRequest, "page count is invalid") + ErrClientTimezoneOffsetInvalid = NewNormalError(NormalSubcategoryGlobal, 8, http.StatusBadRequest, "client timezone offset is invalid") ) // GetParameterInvalidMessage returns specific error message for invalid parameter error diff --git a/pkg/models/transaction.go b/pkg/models/transaction.go index 6f257675..870217b2 100644 --- a/pkg/models/transaction.go +++ b/pkg/models/transaction.go @@ -56,13 +56,13 @@ type TransactionCreateRequest struct { Type TransactionType `json:"type" binding:"required"` CategoryId int64 `json:"categoryId,string"` Time int64 `json:"time" binding:"required,min=1"` + UtcOffset int16 `json:"utcOffset" binding:"min=-720,max=840"` SourceAccountId int64 `json:"sourceAccountId,string" binding:"required,min=1"` DestinationAccountId int64 `json:"destinationAccountId,string" binding:"min=0"` SourceAmount int64 `json:"sourceAmount" binding:"min=-99999999999,max=99999999999"` DestinationAmount int64 `json:"destinationAmount" binding:"min=-99999999999,max=99999999999"` TagIds []string `json:"tagIds"` Comment string `json:"comment" binding:"max=255"` - UtcOffset int `json:"utcOffset" binding:"required,min=-720,max=840"` } // TransactionModifyRequest represents all parameters of transaction modification request @@ -70,13 +70,13 @@ type TransactionModifyRequest struct { Id int64 `json:"id,string" binding:"required,min=1"` CategoryId int64 `json:"categoryId,string"` Time int64 `json:"time" binding:"required,min=1"` + UtcOffset int16 `json:"utcOffset" binding:"min=-720,max=840"` SourceAccountId int64 `json:"sourceAccountId,string" binding:"required,min=1"` DestinationAccountId int64 `json:"destinationAccountId,string" binding:"min=0"` SourceAmount int64 `json:"sourceAmount" binding:"min=-99999999999,max=99999999999"` DestinationAmount int64 `json:"destinationAmount" binding:"min=-99999999999,max=99999999999"` TagIds []string `json:"tagIds"` Comment string `json:"comment" binding:"max=255"` - UtcOffset int `json:"utcOffset" binding:"required,min=-720,max=840"` } // TransactionListByMaxTimeRequest represents all parameters of transaction listing by max time request @@ -88,7 +88,6 @@ type TransactionListByMaxTimeRequest struct { MaxTime int64 `form:"max_time" binding:"min=0"` MinTime int64 `form:"min_time" binding:"min=0"` Count int `form:"count" binding:"required,min=1,max=50"` - UtcOffset int `form:"utc_offset" binding:"required,min=-720,max=840"` } // TransactionListInMonthByPageRequest represents all parameters of transaction listing by month request @@ -101,19 +100,16 @@ type TransactionListInMonthByPageRequest struct { Keyword string `form:"keyword"` Page int `form:"page" binding:"required,min=1"` Count int `form:"count" binding:"required,min=1,max=50"` - UtcOffset int `form:"utc_offset" binding:"required,min=-720,max=840"` } // TransactionGetRequest represents all parameters of transaction getting request type TransactionGetRequest struct { Id int64 `form:"id,string" binding:"required,min=1"` - UtcOffset int `form:"utc_offset" binding:"required,min=-720,max=840"` } // TransactionDeleteRequest represents all parameters of transaction deleting request type TransactionDeleteRequest struct { Id int64 `json:"id,string" binding:"required,min=1"` - UtcOffset int `form:"utc_offset" binding:"required,min=-720,max=840"` } // TransactionInfoResponse represents a view-object of transaction @@ -139,7 +135,7 @@ type TransactionInfoPageWrapperResponse struct { } // IsEditable returns whether this transaction can be edited -func (t *Transaction) IsEditable(currentUser *User, utcOffset int, account *Account, relatedAccount *Account) bool { +func (t *Transaction) IsEditable(currentUser *User, utcOffset int16, account *Account, relatedAccount *Account) bool { if currentUser == nil || !currentUser.CanEditTransactionByTransactionTime(t.TransactionTime, utcOffset) { return false } diff --git a/pkg/models/user.go b/pkg/models/user.go index 914f42bf..ed40c2a2 100644 --- a/pkg/models/user.go +++ b/pkg/models/user.go @@ -124,7 +124,7 @@ type UserProfileResponse struct { } // CanEditTransactionByTransactionTime returns whether this user can edit transaction with specified transaction time -func (u *User) CanEditTransactionByTransactionTime(transactionTime int64, utcOffset int) bool { +func (u *User) CanEditTransactionByTransactionTime(transactionTime int64, utcOffset int16) bool { if u.TransactionEditScope == TRANSACTION_EDIT_SCOPE_NONE { return false } else if u.TransactionEditScope == TRANSACTION_EDIT_SCOPE_ALL { @@ -141,7 +141,7 @@ func (u *User) CanEditTransactionByTransactionTime(transactionTime int64, utcOff _, serverUtcOffset := now.Zone() serverTodayFirstUnixTime := now.Unix() - int64(now.Hour()*60*60+now.Minute()*60+now.Second()) - clientTodayFirstUnixTime := serverTodayFirstUnixTime + int64(utcOffset*60-serverUtcOffset) + clientTodayFirstUnixTime := serverTodayFirstUnixTime + int64(utcOffset)*60 - int64(serverUtcOffset) if u.TransactionEditScope == TRANSACTION_EDIT_SCOPE_TODAY_OR_LATER { return transactionUnixTime >= clientTodayFirstUnixTime diff --git a/src/lib/services.js b/src/lib/services.js index d01652b0..dac4ed11 100644 --- a/src/lib/services.js +++ b/src/lib/services.js @@ -16,6 +16,8 @@ axios.interceptors.request.use(config => { config.headers.Authorization = `Bearer ${token}`; } + config.headers['X-Timezone-Offset'] = utils.getTimezoneOffsetMinutes(); + if (needBlockRequest && !config.ignoreBlocked) { return new Promise(resolve => { blockedRequests.push(newToken => { @@ -251,12 +253,10 @@ export default { }); }, getTransactions: ({ maxTime, minTime, type, categoryId, accountId, keyword }) => { - const utcOffset = utils.getTimezoneOffsetMinutes(); - return axios.get(`v1/transactions/list.json?max_time=${maxTime}&min_time=${minTime}&type=${type}&category_id=${categoryId}&account_id=${accountId}&keyword=${keyword}&count=50&utc_offset=${utcOffset}`); + return axios.get(`v1/transactions/list.json?max_time=${maxTime}&min_time=${minTime}&type=${type}&category_id=${categoryId}&account_id=${accountId}&keyword=${keyword}&count=50`); }, getTransaction: ({ id }) => { - const utcOffset = utils.getTimezoneOffsetMinutes(); - return axios.get(`v1/transactions/get.json?id=${id}&utc_offset=${utcOffset}`); + return axios.get(`v1/transactions/get.json?id=${id}`); }, addTransaction: ({ type, categoryId, time, sourceAccountId, destinationAccountId, sourceAmount, destinationAmount, tagIds, comment, utcOffset }) => { return axios.post('v1/transactions/add.json', { @@ -288,10 +288,8 @@ export default { }); }, deleteTransaction: ({ id }) => { - const utcOffset = utils.getTimezoneOffsetMinutes(); return axios.post('v1/transactions/delete.json', { - id, - utcOffset + id }); }, getAllTransactionCategories: () => { diff --git a/src/locales/en.js b/src/locales/en.js index 42b0d604..397236fc 100644 --- a/src/locales/en.js +++ b/src/locales/en.js @@ -491,6 +491,7 @@ export default { 'operation failed': 'Operation failed', 'nothing will be updated': 'Nothing will be updated', 'failed to request third party api': 'Failed to request third party api', + 'client timezone offset is invalid': 'Client timezone offset is invalid', 'user id is invalid': 'User ID is invalid', 'username is empty': 'Username is empty', 'email is empty': 'Email is empty', diff --git a/src/locales/zh_Hans.js b/src/locales/zh_Hans.js index d01b9b77..bb753d1b 100644 --- a/src/locales/zh_Hans.js +++ b/src/locales/zh_Hans.js @@ -491,6 +491,7 @@ export default { 'operation failed': '操作失败', 'nothing will be updated': '没有内容更新', 'failed to request third party api': '请求第三方接口失败', + 'client timezone offset is invalid': '客户端时区时间差异无效', 'user id is invalid': '用户ID无效', 'username is empty': '用户名为空', 'email is empty': '电子邮箱为空',