mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge pull request #1665 from gradido/refactor-balance-resolver
refactor: Balance Resolver
This commit is contained in:
commit
5608937d75
@ -24,6 +24,7 @@ export enum RIGHTS {
|
||||
QUERY_TRANSACTION_LINK = 'QUERY_TRANSACTION_LINK',
|
||||
REDEEM_TRANSACTION_LINK = 'REDEEM_TRANSACTION_LINK',
|
||||
LIST_TRANSACTION_LINKS = 'LIST_TRANSACTION_LINKS',
|
||||
GDT_BALANCE = 'GDT_BALANCE',
|
||||
// Admin
|
||||
SEARCH_USERS = 'SEARCH_USERS',
|
||||
CREATE_PENDING_CREATION = 'CREATE_PENDING_CREATION',
|
||||
|
||||
@ -22,6 +22,7 @@ export const ROLE_USER = new Role('user', [
|
||||
RIGHTS.DELETE_TRANSACTION_LINK,
|
||||
RIGHTS.REDEEM_TRANSACTION_LINK,
|
||||
RIGHTS.LIST_TRANSACTION_LINKS,
|
||||
RIGHTS.GDT_BALANCE,
|
||||
])
|
||||
export const ROLE_ADMIN = new Role('admin', Object.values(RIGHTS)) // all rights
|
||||
|
||||
|
||||
@ -1,22 +1,55 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { ObjectType, Field } from 'type-graphql'
|
||||
import Decimal from 'decimal.js-light'
|
||||
import CONFIG from '@/config'
|
||||
|
||||
@ObjectType()
|
||||
export class Balance {
|
||||
constructor(json: any) {
|
||||
this.balance = json.balance
|
||||
this.decay = json.decay
|
||||
this.decayDate = json.decay_date
|
||||
constructor(data: {
|
||||
balance: Decimal
|
||||
decay: Decimal
|
||||
lastBookedBalance: Decimal
|
||||
balanceGDT: number | null
|
||||
count: number
|
||||
linkCount: number
|
||||
decayStartBlock?: Date
|
||||
lastBookedDate?: Date | null
|
||||
}) {
|
||||
this.balance = data.balance
|
||||
this.decay = data.decay
|
||||
this.lastBookedBalance = data.lastBookedBalance
|
||||
this.balanceGDT = data.balanceGDT || null
|
||||
this.count = data.count
|
||||
this.linkCount = data.linkCount
|
||||
this.decayStartBlock = data.decayStartBlock || CONFIG.DECAY_START_TIME
|
||||
this.lastBookedDate = data.lastBookedDate || null
|
||||
}
|
||||
|
||||
// the actual balance, decay included
|
||||
@Field(() => Decimal)
|
||||
balance: Decimal
|
||||
|
||||
// the decay since the last booked balance
|
||||
@Field(() => Decimal)
|
||||
decay: Decimal
|
||||
|
||||
@Field(() => Decimal)
|
||||
lastBookedBalance: Decimal
|
||||
|
||||
@Field(() => Number, { nullable: true })
|
||||
balanceGDT: number | null
|
||||
|
||||
// the count of all transactions
|
||||
@Field(() => Number)
|
||||
count: number
|
||||
|
||||
// the count of transaction links
|
||||
@Field(() => Number)
|
||||
linkCount: number
|
||||
|
||||
@Field(() => Date)
|
||||
decayDate: Date
|
||||
decayStartBlock: Date
|
||||
|
||||
// may be null as there may be no transaction
|
||||
@Field(() => Date, { nullable: true })
|
||||
lastBookedDate: Date | null
|
||||
}
|
||||
|
||||
@ -1,40 +1,16 @@
|
||||
import { ObjectType, Field } from 'type-graphql'
|
||||
import CONFIG from '@/config'
|
||||
import Decimal from 'decimal.js-light'
|
||||
import { Transaction } from './Transaction'
|
||||
import { Balance } from './Balance'
|
||||
|
||||
@ObjectType()
|
||||
export class TransactionList {
|
||||
constructor(
|
||||
balance: Decimal,
|
||||
transactions: Transaction[],
|
||||
count: number,
|
||||
linkCount: number,
|
||||
balanceGDT?: number | null,
|
||||
decayStartBlock: Date = CONFIG.DECAY_START_TIME,
|
||||
) {
|
||||
constructor(balance: Balance, transactions: Transaction[]) {
|
||||
this.balance = balance
|
||||
this.transactions = transactions
|
||||
this.count = count
|
||||
this.linkCount = linkCount
|
||||
this.balanceGDT = balanceGDT || null
|
||||
this.decayStartBlock = decayStartBlock
|
||||
}
|
||||
|
||||
@Field(() => Number, { nullable: true })
|
||||
balanceGDT: number | null
|
||||
|
||||
@Field(() => Number)
|
||||
count: number
|
||||
|
||||
@Field(() => Number)
|
||||
linkCount: number
|
||||
|
||||
@Field(() => Decimal)
|
||||
balance: Decimal
|
||||
|
||||
@Field(() => Date)
|
||||
decayStartBlock: Date
|
||||
@Field(() => Balance)
|
||||
balance: Balance
|
||||
|
||||
@Field(() => [Transaction])
|
||||
transactions: Transaction[]
|
||||
|
||||
@ -5,36 +5,74 @@ import { Resolver, Query, Ctx, Authorized } from 'type-graphql'
|
||||
import { Balance } from '@model/Balance'
|
||||
import { calculateDecay } from '@/util/decay'
|
||||
import { RIGHTS } from '@/auth/RIGHTS'
|
||||
import { Transaction } from '@entity/Transaction'
|
||||
import { Transaction as dbTransaction } from '@entity/Transaction'
|
||||
import Decimal from 'decimal.js-light'
|
||||
import { GdtResolver } from './GdtResolver'
|
||||
import { TransactionLink as dbTransactionLink } from '@entity/TransactionLink'
|
||||
import { MoreThan, getCustomRepository } from '@dbTools/typeorm'
|
||||
import { TransactionLinkRepository } from '@repository/TransactionLink'
|
||||
|
||||
@Resolver()
|
||||
export class BalanceResolver {
|
||||
@Authorized([RIGHTS.BALANCE])
|
||||
@Query(() => Balance)
|
||||
async balance(@Ctx() context: any): Promise<Balance> {
|
||||
// load user and balance
|
||||
const { user } = context
|
||||
const now = new Date()
|
||||
|
||||
const lastTransaction = await Transaction.findOne(
|
||||
{ userId: user.id },
|
||||
{ order: { balanceDate: 'DESC' } },
|
||||
)
|
||||
const gdtResolver = new GdtResolver()
|
||||
const balanceGDT = await gdtResolver.gdtBalance(context)
|
||||
|
||||
const lastTransaction = context.lastTransaction
|
||||
? context.lastTransaction
|
||||
: await dbTransaction.findOne({ userId: user.id }, { order: { balanceDate: 'DESC' } })
|
||||
|
||||
// No balance found
|
||||
if (!lastTransaction) {
|
||||
return new Balance({
|
||||
balance: new Decimal(0),
|
||||
decay: new Decimal(0),
|
||||
decay_date: now.toString(),
|
||||
lastBookedBalance: new Decimal(0),
|
||||
balanceGDT,
|
||||
count: 0,
|
||||
linkCount: 0,
|
||||
})
|
||||
}
|
||||
|
||||
const count =
|
||||
context.transactionCount || context.transactionCount === 0
|
||||
? context.transactionCount
|
||||
: await dbTransaction.count({ where: { userId: user.id } })
|
||||
const linkCount =
|
||||
context.linkCount || context.linkCount === 0
|
||||
? context.linkCount
|
||||
: await dbTransactionLink.count({
|
||||
where: {
|
||||
userId: user.id,
|
||||
redeemedAt: null,
|
||||
validUntil: MoreThan(new Date()),
|
||||
},
|
||||
})
|
||||
|
||||
const transactionLinkRepository = getCustomRepository(TransactionLinkRepository)
|
||||
const { sumHoldAvailableAmount } = context.sumHoldAvailableAmount
|
||||
? { sumHoldAvailableAmount: context.sumHoldAvailableAmount }
|
||||
: await transactionLinkRepository.summary(user.id, now)
|
||||
|
||||
const calculatedDecay = calculateDecay(
|
||||
lastTransaction.balance.minus(sumHoldAvailableAmount.toString()),
|
||||
lastTransaction.balanceDate,
|
||||
now,
|
||||
)
|
||||
|
||||
return new Balance({
|
||||
balance: lastTransaction.balance,
|
||||
decay: calculateDecay(lastTransaction.balance, lastTransaction.balanceDate, now).balance,
|
||||
decay_date: now.toString(),
|
||||
balance: calculatedDecay.balance.toDecimalPlaces(2, Decimal.ROUND_DOWN), // round towards zero
|
||||
decay: calculatedDecay.decay.toDecimalPlaces(2, Decimal.ROUND_FLOOR), // round towards - infinity
|
||||
lastBookedBalance: lastTransaction.balance.toDecimalPlaces(2, Decimal.ROUND_DOWN),
|
||||
balanceGDT,
|
||||
count,
|
||||
linkCount,
|
||||
lastBookedDate: lastTransaction.balanceDate,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@ import { Resolver, Query, Args, Ctx, Authorized, Arg } from 'type-graphql'
|
||||
import CONFIG from '@/config'
|
||||
import { GdtEntryList } from '@model/GdtEntryList'
|
||||
import Paginated from '@arg/Paginated'
|
||||
import { apiGet } from '@/apis/HttpRequest'
|
||||
import { apiGet, apiPost } from '@/apis/HttpRequest'
|
||||
import { Order } from '@enum/Order'
|
||||
import { RIGHTS } from '@/auth/RIGHTS'
|
||||
|
||||
@ -13,13 +13,11 @@ import { RIGHTS } from '@/auth/RIGHTS'
|
||||
export class GdtResolver {
|
||||
@Authorized([RIGHTS.LIST_GDT_ENTRIES])
|
||||
@Query(() => GdtEntryList)
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
async listGDTEntries(
|
||||
@Args()
|
||||
{ currentPage = 1, pageSize = 5, order = Order.DESC }: Paginated,
|
||||
@Ctx() context: any,
|
||||
): Promise<GdtEntryList> {
|
||||
// load user
|
||||
const userEntity = context.user
|
||||
|
||||
try {
|
||||
@ -35,6 +33,25 @@ export class GdtResolver {
|
||||
}
|
||||
}
|
||||
|
||||
@Authorized([RIGHTS.GDT_BALANCE])
|
||||
@Query(() => Number)
|
||||
async gdtBalance(@Ctx() context: any): Promise<number | null> {
|
||||
const { user } = context
|
||||
try {
|
||||
const resultGDTSum = await apiPost(`${CONFIG.GDT_API_URL}/GdtEntries/sumPerEmailApi`, {
|
||||
email: user.email,
|
||||
})
|
||||
if (!resultGDTSum.success) {
|
||||
throw new Error('Call not successful')
|
||||
}
|
||||
return Number(resultGDTSum.data.sum) || 0
|
||||
} catch (err: any) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Could not query GDT Server', err)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
@Authorized([RIGHTS.EXIST_PID])
|
||||
@Query(() => Number)
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
||||
@ -6,7 +6,6 @@
|
||||
import { Resolver, Query, Args, Authorized, Ctx, Mutation } from 'type-graphql'
|
||||
import { getCustomRepository, getConnection } from '@dbTools/typeorm'
|
||||
|
||||
import CONFIG from '@/config'
|
||||
import { sendTransactionReceivedEmail } from '@/mailer/sendTransactionReceivedEmail'
|
||||
|
||||
import { Transaction } from '@model/Transaction'
|
||||
@ -24,7 +23,6 @@ import { User as dbUser } from '@entity/User'
|
||||
import { Transaction as dbTransaction } from '@entity/Transaction'
|
||||
import { TransactionLink as dbTransactionLink } from '@entity/TransactionLink'
|
||||
|
||||
import { apiPost } from '@/apis/HttpRequest'
|
||||
import { TransactionTypeId } from '@enum/TransactionTypeId'
|
||||
import { calculateBalance, isHexPublicKey } from '@/util/validate'
|
||||
import { RIGHTS } from '@/auth/RIGHTS'
|
||||
@ -32,7 +30,8 @@ import { User } from '@model/User'
|
||||
import { communityUser } from '@/util/communityUser'
|
||||
import { virtualLinkTransaction, virtualDecayTransaction } from '@/util/virtualTransactions'
|
||||
import Decimal from 'decimal.js-light'
|
||||
import { calculateDecay } from '@/util/decay'
|
||||
|
||||
import { BalanceResolver } from './BalanceResolver'
|
||||
|
||||
const MEMO_MAX_CHARS = 255
|
||||
const MEMO_MIN_CHARS = 5
|
||||
@ -154,23 +153,11 @@ export class TransactionResolver {
|
||||
{ order: { balanceDate: 'DESC' } },
|
||||
)
|
||||
|
||||
// get GDT
|
||||
let balanceGDT = null
|
||||
try {
|
||||
const resultGDTSum = await apiPost(`${CONFIG.GDT_API_URL}/GdtEntries/sumPerEmailApi`, {
|
||||
email: user.email,
|
||||
})
|
||||
if (!resultGDTSum.success) {
|
||||
throw new Error('Call not successful')
|
||||
}
|
||||
balanceGDT = Number(resultGDTSum.data.sum) || 0
|
||||
} catch (err: any) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Could not query GDT Server', err)
|
||||
}
|
||||
const balanceResolver = new BalanceResolver()
|
||||
context.lastTransaction = lastTransaction
|
||||
|
||||
if (!lastTransaction) {
|
||||
return new TransactionList(new Decimal(0), [], 0, 0, balanceGDT)
|
||||
return new TransactionList(await balanceResolver.balance(context), [])
|
||||
}
|
||||
|
||||
// find transactions
|
||||
@ -183,6 +170,7 @@ export class TransactionResolver {
|
||||
offset,
|
||||
order,
|
||||
)
|
||||
context.transactionCount = userTransactionsCount
|
||||
|
||||
// find involved users; I am involved
|
||||
const involvedUserIds: number[] = [user.id]
|
||||
@ -205,6 +193,8 @@ export class TransactionResolver {
|
||||
const transactionLinkRepository = getCustomRepository(TransactionLinkRepository)
|
||||
const { sumHoldAvailableAmount, sumAmount, lastDate, firstDate, transactionLinkcount } =
|
||||
await transactionLinkRepository.summary(user.id, now)
|
||||
context.linkCount = transactionLinkcount
|
||||
context.sumHoldAvailableAmount = sumHoldAvailableAmount
|
||||
|
||||
// decay & link transactions
|
||||
if (currentPage === 1 && order === Order.DESC) {
|
||||
@ -237,15 +227,7 @@ export class TransactionResolver {
|
||||
})
|
||||
|
||||
// Construct Result
|
||||
return new TransactionList(
|
||||
calculateDecay(lastTransaction.balance, lastTransaction.balanceDate, now).balance.minus(
|
||||
sumHoldAvailableAmount.toString(),
|
||||
),
|
||||
transactions,
|
||||
userTransactionsCount,
|
||||
transactionLinkcount,
|
||||
balanceGDT,
|
||||
)
|
||||
return new TransactionList(await balanceResolver.balance(context), transactions)
|
||||
}
|
||||
|
||||
@Authorized([RIGHTS.SEND_COINS])
|
||||
|
||||
@ -45,11 +45,16 @@ export const logout = gql`
|
||||
export const transactionsQuery = gql`
|
||||
query($currentPage: Int = 1, $pageSize: Int = 25, $order: Order = DESC) {
|
||||
transactionList(currentPage: $currentPage, pageSize: $pageSize, order: $order) {
|
||||
balanceGDT
|
||||
count
|
||||
linkCount
|
||||
balance
|
||||
decayStartBlock
|
||||
balance {
|
||||
balance
|
||||
decay
|
||||
lastBookedBalance
|
||||
balanceGDT
|
||||
count
|
||||
linkCount
|
||||
decayStartBlock
|
||||
lastBookedDate
|
||||
}
|
||||
transactions {
|
||||
id
|
||||
typeId
|
||||
|
||||
@ -145,11 +145,13 @@ describe('DashboardLayoutGdd', () => {
|
||||
apolloMock.mockResolvedValue({
|
||||
data: {
|
||||
transactionList: {
|
||||
balanceGDT: 100,
|
||||
count: 4,
|
||||
linkCount: 8,
|
||||
balance: 1450,
|
||||
decay: 1250,
|
||||
balance: {
|
||||
balanceGDT: 100,
|
||||
count: 4,
|
||||
linkCount: 8,
|
||||
balance: 1450,
|
||||
decay: 1250,
|
||||
},
|
||||
transactions: ['transaction', 'transaction', 'transaction', 'transaction'],
|
||||
},
|
||||
},
|
||||
|
||||
@ -103,12 +103,14 @@ export default {
|
||||
data: { transactionList },
|
||||
} = result
|
||||
this.GdtBalance =
|
||||
transactionList.balanceGDT === null ? null : Number(transactionList.balanceGDT)
|
||||
transactionList.balance.balanceGDT === null
|
||||
? null
|
||||
: Number(transactionList.balance.balanceGDT)
|
||||
this.transactions = transactionList.transactions
|
||||
this.balance = Number(transactionList.balance)
|
||||
this.transactionCount = transactionList.count
|
||||
this.transactionLinkCount = transactionList.linkCount
|
||||
this.decayStartBlock = new Date(transactionList.decayStartBlock)
|
||||
this.balance = Number(transactionList.balance.balance)
|
||||
this.transactionCount = transactionList.balance.count
|
||||
this.transactionLinkCount = transactionList.balance.linkCount
|
||||
this.decayStartBlock = new Date(transactionList.balance.decayStartBlock)
|
||||
this.pending = false
|
||||
})
|
||||
.catch((error) => {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user