60 lines
2.0 KiB
TypeScript
60 lines
2.0 KiB
TypeScript
import { query } from '../../db/client.js'
|
|
import { TASK_STATUS } from '../../domain/task-status.js'
|
|
import { nowIso } from '../../utils/time.js'
|
|
import type { JsonObject } from '../../types/json.js'
|
|
|
|
export async function getAdminDashboardSummary() {
|
|
const todayPrefix = nowIso().slice(0, 10)
|
|
const paidPendingClaimStatuses = [
|
|
TASK_STATUS.PAID,
|
|
TASK_STATUS.LINK_GENERATED,
|
|
TASK_STATUS.PENDING_BINDING_PREPARE,
|
|
TASK_STATUS.WAITING_BINDING,
|
|
]
|
|
const claimingStatuses = [TASK_STATUS.CLAIMED, TASK_STATUS.ROLE_CONFIRMED, TASK_STATUS.REDEEMING]
|
|
const abnormalStatuses = [TASK_STATUS.RETRY_PENDING, TASK_STATUS.MANUAL_REVIEW]
|
|
const result = await query(
|
|
`
|
|
SELECT
|
|
(SELECT COUNT(*)::int FROM orders WHERE to_char(created_at AT TIME ZONE 'Asia/Shanghai', 'YYYY-MM-DD') = $1) AS today_orders,
|
|
(
|
|
SELECT COUNT(*)::int
|
|
FROM fulfillment_tasks
|
|
WHERE task_status = ANY($2::text[])
|
|
) AS paid_pending_claim,
|
|
(
|
|
SELECT COUNT(*)::int
|
|
FROM fulfillment_tasks
|
|
WHERE task_status = ANY($3::text[])
|
|
) AS claiming_tasks,
|
|
(
|
|
SELECT COUNT(*)::int
|
|
FROM fulfillment_tasks
|
|
WHERE task_status = $4
|
|
AND to_char(updated_at AT TIME ZONE 'Asia/Shanghai', 'YYYY-MM-DD') = $1
|
|
) AS redeemed_today,
|
|
(
|
|
SELECT COUNT(*)::int
|
|
FROM fulfillment_tasks
|
|
WHERE task_status = ANY($5::text[])
|
|
) AS abnormal_tasks
|
|
`,
|
|
[
|
|
todayPrefix,
|
|
paidPendingClaimStatuses,
|
|
claimingStatuses,
|
|
TASK_STATUS.REDEEMED,
|
|
abnormalStatuses,
|
|
],
|
|
)
|
|
const summary: JsonObject = result.rows[0] || {}
|
|
|
|
return {
|
|
todayOrders: Number(summary?.today_orders || 0),
|
|
paidPendingClaim: Number(summary?.paid_pending_claim || 0),
|
|
claimingTasks: Number(summary?.claiming_tasks || 0),
|
|
redeemedToday: Number(summary?.redeemed_today || 0),
|
|
abnormalTasks: Number(summary?.abnormal_tasks || 0),
|
|
}
|
|
}
|