添加信用额度功能:在账户模型中新增信用额度字段,更新相关请求和响应结构,修改账户创建和修改逻辑,更新界面以支持信用额度的显示和编辑。

This commit is contained in:
2026-04-05 17:04:16 +08:00
parent 285fef6eba
commit 5fbff39c4f
7 changed files with 102 additions and 8 deletions
+12
View File
@@ -713,6 +713,9 @@ func (a *AccountsApi) createNewAccountModel(uid int64, accountCreateReq *models.
if !isSubAccount && accountCreateReq.Category == models.ACCOUNT_CATEGORY_CREDIT_CARD {
accountExtend.CreditCardStatementDate = &accountCreateReq.CreditCardStatementDate
if accountCreateReq.CreditLimit > 0 {
accountExtend.CreditLimit = &accountCreateReq.CreditLimit
}
}
return &models.Account{
@@ -769,6 +772,9 @@ func (a *AccountsApi) getToUpdateAccount(uid int64, accountModifyReq *models.Acc
if !isSubAccount && accountModifyReq.Category == models.ACCOUNT_CATEGORY_CREDIT_CARD {
newAccountExtend.CreditCardStatementDate = &accountModifyReq.CreditCardStatementDate
if accountModifyReq.CreditLimit > 0 {
newAccountExtend.CreditLimit = &accountModifyReq.CreditLimit
}
}
newAccount := &models.Account{
@@ -804,6 +810,12 @@ func (a *AccountsApi) getToUpdateAccount(uid int64, accountModifyReq *models.Acc
return newAccount
}
if newAccountExtend.CreditLimit != oldAccountExtend.CreditLimit {
if newAccountExtend.CreditLimit == nil || oldAccountExtend.CreditLimit == nil || *newAccountExtend.CreditLimit != *oldAccountExtend.CreditLimit {
return newAccount
}
}
return nil
}
+10 -1
View File
@@ -90,7 +90,8 @@ type Account struct {
// AccountExtend represents account extend data stored in database
type AccountExtend struct {
CreditCardStatementDate *int `json:"creditCardStatementDate"`
CreditCardStatementDate *int `json:"creditCardStatementDate"`
CreditLimit *int64 `json:"creditLimit"`
}
// AccountCreateRequest represents all parameters of account creation request
@@ -105,6 +106,7 @@ type AccountCreateRequest struct {
BalanceTime int64 `json:"balanceTime"`
Comment string `json:"comment" binding:"max=255"`
CreditCardStatementDate int `json:"creditCardStatementDate" binding:"min=0,max=28"`
CreditLimit int64 `json:"creditLimit" binding:"min=0"`
SubAccounts []*AccountCreateRequest `json:"subAccounts" binding:"omitempty"`
ClientSessionId string `json:"clientSessionId"`
}
@@ -121,6 +123,7 @@ type AccountModifyRequest struct {
BalanceTime *int64 `json:"balanceTime" binding:"omitempty"`
Comment string `json:"comment" binding:"max=255"`
CreditCardStatementDate int `json:"creditCardStatementDate" binding:"min=0,max=28"`
CreditLimit int64 `json:"creditLimit" binding:"min=0"`
Hidden bool `json:"hidden"`
SubAccounts []*AccountModifyRequest `json:"subAccounts" binding:"omitempty"`
ClientSessionId string `json:"clientSessionId"`
@@ -171,6 +174,7 @@ type AccountInfoResponse struct {
Balance int64 `json:"balance"`
Comment string `json:"comment"`
CreditCardStatementDate *int `json:"creditCardStatementDate,omitempty"`
CreditLimit *int64 `json:"creditLimit,omitempty"`
DisplayOrder int32 `json:"displayOrder"`
IsAsset bool `json:"isAsset,omitempty"`
IsLiability bool `json:"isLiability,omitempty"`
@@ -181,10 +185,14 @@ type AccountInfoResponse struct {
// ToAccountInfoResponse returns a view-object according to database model
func (a *Account) ToAccountInfoResponse() *AccountInfoResponse {
var creditCardStatementDate *int
var creditLimit *int64
if a.ParentAccountId == LevelOneAccountParentId && a.Category == ACCOUNT_CATEGORY_CREDIT_CARD {
if a.Extend != nil {
creditCardStatementDate = a.Extend.CreditCardStatementDate
if a.Extend.CreditLimit != nil && *a.Extend.CreditLimit > 0 {
creditLimit = a.Extend.CreditLimit
}
} else {
creditCardStatementDate = &defaultCreditCardAccountStatementDate
}
@@ -202,6 +210,7 @@ func (a *Account) ToAccountInfoResponse() *AccountInfoResponse {
Balance: a.Balance,
Comment: a.Comment,
CreditCardStatementDate: creditCardStatementDate,
CreditLimit: creditLimit,
DisplayOrder: a.DisplayOrder,
IsAsset: assetAccountCategory[a.Category],
IsLiability: liabilityAccountCategory[a.Category],
+2
View File
@@ -1860,6 +1860,8 @@
"Balance Time": "Balance Time",
"Sub-account Balance Time": "Sub-account Balance Time",
"Statement Date": "Statement Date",
"Credit Limit": "Credit Limit",
"Available": "Available",
"Description": "Description",
"Your account description (optional)": "Your account description (optional)",
"Your sub-account description (optional)": "Your sub-account description (optional)",
+2
View File
@@ -1860,6 +1860,8 @@
"Balance Time": "余额时间",
"Sub-account Balance Time": "子账户余额时间",
"Statement Date": "账单日",
"Credit Limit": "信用额度",
"Available": "可用额度",
"Description": "描述",
"Your account description (optional)": "你的账户描述 (可选)",
"Your sub-account description (optional)": "你的子账户描述 (可选)",
+21 -6
View File
@@ -17,6 +17,7 @@ export class Account implements AccountInfoResponse {
public balanceTime?: number;
public comment: string;
public creditCardStatementDate?: number;
public creditLimit?: number;
public displayOrder: number;
public visible: boolean;
public subAccounts?: Account[];
@@ -24,7 +25,7 @@ export class Account implements AccountInfoResponse {
private readonly _isAsset?: boolean;
private readonly _isLiability?: boolean;
protected constructor(id: string, name: string, parentId: string, category: number, type: number, icon: string, color: string, currency: string, balance: number, comment: string, displayOrder: number, visible: boolean, balanceTime?: number, creditCardStatementDate?: number, isAsset?: boolean, isLiability?: boolean, subAccounts?: Account[]) {
protected constructor(id: string, name: string, parentId: string, category: number, type: number, icon: string, color: string, currency: string, balance: number, comment: string, displayOrder: number, visible: boolean, balanceTime?: number, creditCardStatementDate?: number, isAsset?: boolean, isLiability?: boolean, subAccounts?: Account[], creditLimit?: number) {
this.id = id;
this.name = name;
this.parentId = parentId;
@@ -39,6 +40,7 @@ export class Account implements AccountInfoResponse {
this.displayOrder = displayOrder;
this.visible = visible;
this.creditCardStatementDate = creditCardStatementDate;
this.creditLimit = creditLimit;
this._isAsset = isAsset;
this._isLiability = isLiability;
@@ -95,7 +97,8 @@ export class Account implements AccountInfoResponse {
this.comment === other.comment &&
this.displayOrder === other.displayOrder &&
this.visible === other.visible &&
this.creditCardStatementDate === other.creditCardStatementDate;
this.creditCardStatementDate === other.creditCardStatementDate &&
this.creditLimit === other.creditLimit;
if (!isEqual) {
return false;
@@ -130,6 +133,7 @@ export class Account implements AccountInfoResponse {
this.balanceTime = other.balanceTime;
this.comment = other.comment;
this.creditCardStatementDate = other.creditCardStatementDate;
this.creditLimit = other.creditLimit;
this.visible = other.visible;
}
@@ -181,6 +185,7 @@ export class Account implements AccountInfoResponse {
balanceTime: (parentAccount || this.type === AccountType.SingleAccount.type) && this.balanceTime ? this.balanceTime : 0,
comment: this.comment,
creditCardStatementDate: !parentAccount && this.category === AccountCategory.CreditCard.type ? this.creditCardStatementDate : undefined,
creditLimit: !parentAccount && this.category === AccountCategory.CreditCard.type ? (this.creditLimit ?? 0) : undefined,
subAccounts: !parentAccount ? subAccountCreateRequests : undefined,
clientSessionId: !parentAccount ? clientSessionId : undefined
};
@@ -214,6 +219,7 @@ export class Account implements AccountInfoResponse {
balanceTime: parentAccount && (!this.id || this.id === '0') ? this.balanceTime : undefined,
comment: this.comment,
creditCardStatementDate: !parentAccount && this.category === AccountCategory.CreditCard.type ? this.creditCardStatementDate : undefined,
creditLimit: !parentAccount && this.category === AccountCategory.CreditCard.type ? (this.creditLimit ?? 0) : undefined,
hidden: !this.visible,
subAccounts: !parentAccount ? subAccountModifyRequests : undefined,
clientSessionId: !parentAccount ? clientSessionId : undefined
@@ -365,7 +371,9 @@ export class Account implements AccountInfoResponse {
this.balanceTime,
this.creditCardStatementDate,
this.isAsset,
this.isLiability
this.isLiability,
undefined,
this.creditLimit
);
}
@@ -387,7 +395,9 @@ export class Account implements AccountInfoResponse {
this.creditCardStatementDate,
this.isAsset,
this.isLiability,
typeof(this.subAccounts) !== 'undefined' ? Account.cloneAccounts(this.subAccounts) : undefined);
typeof(this.subAccounts) !== 'undefined' ? Account.cloneAccounts(this.subAccounts) : undefined,
this.creditLimit
);
}
public createNewSubAccount(currency: string, balanceTime: number): Account {
@@ -446,7 +456,8 @@ export class Account implements AccountInfoResponse {
accountResponse.creditCardStatementDate,
accountResponse.isAsset,
accountResponse.isLiability,
accountResponse.subAccounts ? Account.ofMulti(accountResponse.subAccounts) : undefined
accountResponse.subAccounts ? Account.ofMulti(accountResponse.subAccounts) : undefined,
accountResponse.creditLimit
);
}
@@ -560,7 +571,8 @@ export class AccountWithDisplayBalance extends Account {
account.creditCardStatementDate,
account.isAsset,
account.isLiability,
account.subAccounts
account.subAccounts,
account.creditLimit
);
this.displayBalance = displayBalance;
@@ -582,6 +594,7 @@ export interface AccountCreateRequest {
readonly balanceTime: number;
readonly comment: string;
readonly creditCardStatementDate?: number;
readonly creditLimit?: number;
readonly subAccounts?: AccountCreateRequest[];
readonly clientSessionId?: string;
}
@@ -597,6 +610,7 @@ export interface AccountModifyRequest {
readonly balanceTime?: number;
readonly comment: string;
readonly creditCardStatementDate?: number;
readonly creditLimit?: number;
readonly hidden: boolean;
readonly subAccounts?: AccountModifyRequest[];
readonly clientSessionId?: string;
@@ -614,6 +628,7 @@ export interface AccountInfoResponse {
readonly balance: number;
readonly comment: string;
readonly creditCardStatementDate?: number;
readonly creditLimit?: number;
readonly displayOrder: number;
readonly isAsset?: boolean;
readonly isLiability?: boolean;
+45
View File
@@ -201,6 +201,24 @@
</list-item-selection-popup>
</f7-list-item>
<f7-list-item
link="#" no-chevron
class="list-item-with-header-and-title"
:header="tt('Credit Limit')"
:title="formatCreditLimitDisplay(account)"
v-if="isAccountSupportCreditCardStatementDate"
@click="accountContext.showCreditLimitSheet = true"
>
<number-pad-sheet :min-value="0"
:max-value="TRANSACTION_MAX_AMOUNT"
:currency="account.currency"
:flip-negative="false"
v-model:show="accountContext.showCreditLimitSheet"
:model-value="account.creditLimit ?? 0"
@update:model-value="account.creditLimit = $event > 0 ? $event : undefined"
></number-pad-sheet>
</f7-list-item>
<f7-list-item
link="#" no-chevron
class="list-item-with-header-and-title"
@@ -334,6 +352,24 @@
</list-item-selection-popup>
</f7-list-item>
<f7-list-item
link="#" no-chevron
class="list-item-with-header-and-title"
:header="tt('Credit Limit')"
:title="formatCreditLimitDisplay(account)"
v-if="isAccountSupportCreditCardStatementDate"
@click="accountContext.showCreditLimitSheet = true"
>
<number-pad-sheet :min-value="0"
:max-value="TRANSACTION_MAX_AMOUNT"
:currency="account.currency"
:flip-negative="false"
v-model:show="accountContext.showCreditLimitSheet"
:model-value="account.creditLimit ?? 0"
@update:model-value="account.creditLimit = $event > 0 ? $event : undefined"
></number-pad-sheet>
</f7-list-item>
<f7-list-item :title="tt('Visible')" v-if="editAccountId">
<f7-toggle :checked="account.visible" @toggle:change="account.visible = $event"></f7-toggle>
</f7-list-item>
@@ -550,6 +586,7 @@ interface AccountContext {
showColorSelectionSheet: boolean;
showCurrencyPopup: boolean;
showCreditCardStatementDatePopup: boolean;
showCreditLimitSheet: boolean;
showBalanceSheet: boolean;
showBalanceDateTimeSheet: boolean;
balanceDateTimeSheetMode: string;
@@ -600,6 +637,7 @@ const DEFAULT_ACCOUNT_CONTEXT: AccountContext = {
showColorSelectionSheet: false,
showCurrencyPopup: false,
showCreditCardStatementDatePopup: false,
showCreditLimitSheet: false,
showBalanceSheet: false,
showBalanceDateTimeSheet: false,
balanceDateTimeSheetMode: 'time'
@@ -621,6 +659,13 @@ function formatAccountDisplayBalance(selectedAccount: Account): string {
return formatAmountToLocalizedNumeralsWithCurrency(balance, selectedAccount.currency);
}
function formatCreditLimitDisplay(selectedAccount: Account): string {
if (!selectedAccount.creditLimit) {
return tt('Not set');
}
return formatAmountToLocalizedNumeralsWithCurrency(selectedAccount.creditLimit, selectedAccount.currency);
}
function formatAccountBalanceDate(account: Account): string {
if (!isDefined(account.balanceTime)) {
return '';
+10 -1
View File
@@ -113,6 +113,7 @@
<div class="nested-list-item-title">
<span>{{ account.name }}</span>
<div class="item-footer" v-if="account.comment">{{ account.comment }}</div>
<div class="item-footer" v-if="account.creditLimit">{{ tt('Available') }}: {{ getRemainingCredit(account) }}</div>
</div>
<div class="nested-list-item-after" v-if="account.type === AccountType.MultiSubAccounts.type">
<span>{{ accountBalance(account) }}</span>
@@ -241,7 +242,7 @@ const props = defineProps<{
f7router: Router.Router;
}>();
const { tt, getCurrentLanguageTextDirection } = useI18n();
const { tt, getCurrentLanguageTextDirection, formatAmountToLocalizedNumeralsWithCurrency } = useI18n();
const { showAlert, showToast, routeBackOnError } = useI18nUIComponents();
const {
@@ -288,6 +289,14 @@ const noAvailableAccount = computed<boolean>(() => {
}
});
function getRemainingCredit(account: Account): string {
if (!account.creditLimit) {
return '';
}
const remaining = account.creditLimit + account.balance; // balance is negative for credit cards
return formatAmountToLocalizedNumeralsWithCurrency(remaining, account.currency);
}
function hasAccount(accountCategory: AccountCategory, visibleOnly: boolean): boolean {
return accountsStore.hasAccount(accountCategory, visibleOnly);
}