Implement billing status notification and enhance user management integration
- Added `NotifyBillingStatusChangedAsync` method to `IRealtimeNotifier` for notifying clients about changes in billing status. - Updated `BillingConfigResumer` to call the new notification method after modifying billing configurations, ensuring users receive real-time updates. - Enhanced `ListUsersQueryHandler` to include a `BillingPendingReview` property in `UserSummaryDto`, indicating if a user has a pending payment request awaiting confirmation. - Refactored various command handlers to utilize `AdvisoryLock` for managing concurrent requests, preventing race conditions in billing operations. - Updated tests to cover new notification behaviors and ensure proper functionality in billing status management.
This commit is contained in:
@@ -167,6 +167,9 @@ export type UserSummaryDto = {
|
||||
activatedAt: string | null
|
||||
billingEnabled: boolean
|
||||
billingPaidUntil: string | null
|
||||
/** Есть Subscription-заявка на оплату, ожидающая решения админа — конфиги не гасятся, пока он не
|
||||
* решит (см. BillingService). Бейдж должен показывать нейтральный статус, а не тревожный "Истекло". */
|
||||
billingPendingReview: boolean
|
||||
}
|
||||
|
||||
export type RoleDto = {
|
||||
|
||||
@@ -132,6 +132,9 @@ const resources = {
|
||||
requestStatus: {
|
||||
AwaitingPayment: 'Ожидает оплаты',
|
||||
AwaitingConfirmation: 'На проверке у администратора',
|
||||
Confirmed: 'Подтверждена',
|
||||
Rejected: 'Отклонена',
|
||||
Cancelled: 'Отменена',
|
||||
},
|
||||
requisites: 'Реквизиты для оплаты',
|
||||
requisitesMissing: 'Реквизиты ещё не настроены администратором.',
|
||||
@@ -674,6 +677,9 @@ const resources = {
|
||||
requestStatus: {
|
||||
AwaitingPayment: 'Awaiting payment',
|
||||
AwaitingConfirmation: 'Under admin review',
|
||||
Confirmed: 'Confirmed',
|
||||
Rejected: 'Rejected',
|
||||
Cancelled: 'Cancelled',
|
||||
},
|
||||
requisites: 'Payment details',
|
||||
requisitesMissing: 'The administrator has not set up payment details yet.',
|
||||
|
||||
@@ -8,6 +8,7 @@ type ConfigTrafficUpdated = { configId: string; usedUpBytes: number; usedDownByt
|
||||
type ConfigStatusChanged = { configId: string; status: ConfigStatus }
|
||||
type UserActivated = { userId: string }
|
||||
type NewsPublished = { id: string; title: string; createdAt: string }
|
||||
type BillingStatusChanged = { userId: string }
|
||||
|
||||
/** Живые обновления по SignalR: точечно патчит кэш TanStack Query вместо инвалидации всего списка. */
|
||||
export function RealtimeProvider({ children }: { children: React.ReactNode }) {
|
||||
@@ -54,10 +55,15 @@ export function RealtimeProvider({ children }: { children: React.ReactNode }) {
|
||||
void queryClient.invalidateQueries({ queryKey: ['news'] })
|
||||
}
|
||||
|
||||
const onBillingStatusChanged = (_payload: BillingStatusChanged) => {
|
||||
void queryClient.invalidateQueries({ queryKey: ['my-billing-status'] })
|
||||
}
|
||||
|
||||
connection.on('configTrafficUpdated', onTrafficUpdated)
|
||||
connection.on('configStatusChanged', onStatusChanged)
|
||||
connection.on('userActivated', onUserActivated)
|
||||
connection.on('newsPublished', onNewsPublished)
|
||||
connection.on('billingStatusChanged', onBillingStatusChanged)
|
||||
|
||||
void startConnection()
|
||||
|
||||
@@ -66,6 +72,7 @@ export function RealtimeProvider({ children }: { children: React.ReactNode }) {
|
||||
connection.off('configStatusChanged', onStatusChanged)
|
||||
connection.off('userActivated', onUserActivated)
|
||||
connection.off('newsPublished', onNewsPublished)
|
||||
connection.off('billingStatusChanged', onBillingStatusChanged)
|
||||
}
|
||||
}, [user, queryClient])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user