백오피스 대시보드 수정

feature-credit
김성경 2026-05-07 13:18:34 +09:00
parent 6b97681606
commit 97a6384a54
3 changed files with 42 additions and 41 deletions

View File

@ -15,15 +15,19 @@ async def get_dashboard_context() -> dict:
async with AsyncSessionLocal() as session:
today_start = datetime.now(TIMEZONE).replace(hour=0, minute=0, second=0, microsecond=0)
total_users = (await session.execute(
select(func.count()).select_from(User).where(User.is_deleted == False)
)).scalar()
pending_charge_requests_count = (await session.execute(
select(func.count()).select_from(CreditChargeRequest)
.where(CreditChargeRequest.status == ChargeRequestStatus.PENDING)
)).scalar()
today_charge = (await session.execute(
select(func.count()).select_from(CreditTransaction)
.where(
CreditTransaction.type == CreditTransactionType.CHARGE,
CreditTransaction.created_at >= today_start,
)
)).scalar()
today_consume = (await session.execute(
select(func.count()).select_from(CreditTransaction)
.where(
@ -32,11 +36,13 @@ async def get_dashboard_context() -> dict:
)
)).scalar()
today_charge = (await session.execute(
select(func.count()).select_from(CreditTransaction)
month_start = datetime.now(TIMEZONE).replace(day=1, hour=0, minute=0, second=0, microsecond=0)
month_consume = (await session.execute(
select(func.coalesce(func.sum(func.abs(CreditTransaction.amount)), 0))
.select_from(CreditTransaction)
.where(
CreditTransaction.type == CreditTransactionType.CHARGE,
CreditTransaction.created_at >= today_start,
CreditTransaction.type == CreditTransactionType.CONSUME,
CreditTransaction.created_at >= month_start,
)
)).scalar()
@ -62,10 +68,10 @@ async def get_dashboard_context() -> dict:
return {
"stats": {
"total_users": total_users,
"pending_charge_requests": pending_charge_requests_count,
"today_consume": today_consume,
"today_charge": today_charge,
"today_consume": today_consume,
"month_consume": month_consume,
},
"pending_requests": pending_requests,
"recent_transactions": recent_transactions,

View File

@ -7,15 +7,7 @@
<div class="col-sm-6 col-lg-3">
<div class="card">
<div class="card-body">
<div class="subheader">전체 사용자</div>
<div class="h1 mb-3">{{ stats.total_users }}</div>
</div>
</div>
</div>
<div class="col-sm-6 col-lg-3">
<div class="card">
<div class="card-body">
<div class="subheader">대기 중 충전 요청</div>
<div class="subheader">대기 중인 요청</div>
<div class="h1 mb-3 text-warning">{{ stats.pending_charge_requests }}</div>
</div>
</div>
@ -23,7 +15,15 @@
<div class="col-sm-6 col-lg-3">
<div class="card">
<div class="card-body">
<div class="subheader">오늘 크레딧 소모</div>
<div class="subheader">오늘 승인한 요청</div>
<div class="h1 mb-3 text-success">{{ stats.today_charge }}</div>
</div>
</div>
</div>
<div class="col-sm-6 col-lg-3">
<div class="card">
<div class="card-body">
<div class="subheader">오늘 소모 크레딧</div>
<div class="h1 mb-3 text-danger">{{ stats.today_consume }}</div>
</div>
</div>
@ -31,19 +31,19 @@
<div class="col-sm-6 col-lg-3">
<div class="card">
<div class="card-body">
<div class="subheader">오늘 충전 승인</div>
<div class="h1 mb-3 text-success">{{ stats.today_charge }}</div>
<div class="subheader">이번 달 소모 크레딧</div>
<div class="h1 mb-3 text-danger">{{ stats.month_consume }}</div>
</div>
</div>
</div>
</div>
</div>
<!-- 대기 중 충전 요청 -->
<!-- 대기 중 요청 -->
<div class="col-12 col-lg-6">
<div class="card">
<div class="card-header">
<h3 class="card-title">대기 중 충전 요청</h3>
<h3 class="card-title">대기 중 요청</h3>
<div class="card-options">
<a href="{{ request.url_for('admin:list', identity='credit-charge-request') }}" class="btn btn-sm btn-primary">전체 보기</a>
</div>

View File

@ -56,8 +56,6 @@ class UserAdmin(SuperAdminEditable, ModelView, model=User):
"last_login_at",
"created_at",
"updated_at",
"credit_requests",
"credit_transactions",
]
form_columns = [
@ -119,61 +117,60 @@ class UserAdmin(SuperAdminEditable, ModelView, model=User):
}
@action(
name="block_user",
name="01_block_user",
label="계정 차단",
confirmation_message="선택한 사용자를 차단하시겠습니까?",
add_in_list=True,
)
async def block_user_action(self, request: Request) -> RedirectResponse:
async def seq_f_block_user_action(self, request: Request) -> RedirectResponse:
return await handle_block_users(request, self.identity, block=True)
@action(
name="unblock_user",
name="02_unblock_user",
label="차단 해제",
confirmation_message="선택한 사용자의 차단을 해제하시겠습니까?",
add_in_list=True,
)
async def unblock_user_action(self, request: Request) -> RedirectResponse:
async def seq_e_unblock_user_action(self, request: Request) -> RedirectResponse:
return await handle_block_users(request, self.identity, block=False)
@action(
name="grant_credits_1",
name="03_grant_credits_1",
label="크레딧 +1",
confirmation_message="선택한 사용자에게 크레딧 1개를 충전하시겠습니까?",
add_in_list=True,
)
async def grant_credits_1_action(self, request: Request) -> RedirectResponse:
async def seq_d_grant_credits_1_action(self, request: Request) -> RedirectResponse:
admin_id = request.session.get("admin_id")
return await handle_grant_credits(request, self.identity, amount=1, admin_id=admin_id)
@action(
name="grant_credits_5",
name="04_grant_credits_5",
label="크레딧 +5",
confirmation_message="선택한 사용자에게 크레딧 5개를 충전하시겠습니까?",
add_in_list=True,
)
async def grant_credits_5_action(self, request: Request) -> RedirectResponse:
async def seq_c_grant_credits_5_action(self, request: Request) -> RedirectResponse:
admin_id = request.session.get("admin_id")
return await handle_grant_credits(request, self.identity, amount=5, admin_id=admin_id)
@action(
name="grant_credits_10",
name="05_grant_credits_10",
label="크레딧 +10",
confirmation_message="선택한 사용자에게 크레딧 10개를 충전하시겠습니까?",
add_in_list=True,
)
async def grant_credits_10_action(self, request: Request) -> RedirectResponse:
async def seq_b_grant_credits_10_action(self, request: Request) -> RedirectResponse:
admin_id = request.session.get("admin_id")
return await handle_grant_credits(request, self.identity, amount=10, admin_id=admin_id)
@action(
name="deduct_credits_1",
name="06_deduct_credits_1",
label="크레딧 -1",
confirmation_message="선택한 사용자의 크레딧 1개를 차감하시겠습니까?",
add_in_list=True,
)
async def deduct_credits_1_action(self, request: Request) -> RedirectResponse:
async def seq_a_deduct_credits_1_action(self, request: Request) -> RedirectResponse:
admin_id = request.session.get("admin_id")
return await handle_deduct_credits(request, self.identity, amount=1, admin_id=admin_id)
@ -201,8 +198,6 @@ class SocialAccountAdmin(ViewerAccessible, ModelView, model=SocialAccount):
"platform",
"platform_user_id",
"platform_username",
"platform_data",
"scope",
"token_expires_at",
"is_active",
"is_deleted",