import { useCallback, useEffect, useMemo, useState } from "react"; import { usePaginatedQuery } from "@/shared/hooks/usePaginatedQuery.js"; import { useToast } from "@/shared/ui/ToastProvider.jsx"; import { emptyUserIdentityFilter, userIdentityFilterParams } from "@/shared/ui/userIdentityFilter.js"; import { listResourceGroups } from "@/features/resources/api"; import { getRegistrationRewardConfig, listRegistrationRewardClaims, updateRegistrationRewardConfig, } from "@/features/registration-reward/api"; import { useRegistrationRewardAbilities } from "@/features/registration-reward/permissions.js"; const pageSize = 50; const emptyClaims = { items: [], page: 1, pageSize, todayClaimedCount: 0, total: 0 }; const emptyTimeRange = { endMs: "", startMs: "" }; const emptyForm = { coinAmount: "", dailyLimit: "0", enabled: false, resourceGroupId: "", rewardType: "coin", }; export function useRegistrationRewardPage() { const abilities = useRegistrationRewardAbilities(); const { showToast } = useToast(); const [config, setConfig] = useState(null); const [configDrawerOpen, setConfigDrawerOpen] = useState(false); const [form, setForm] = useState(emptyForm); const [configLoading, setConfigLoading] = useState(false); const [configSaving, setConfigSaving] = useState(false); const [resourceGroups, setResourceGroups] = useState([]); const [userFilter, setUserFilter] = useState(emptyUserIdentityFilter); const [status, setStatus] = useState(""); const [timeRange, setTimeRange] = useState(emptyTimeRange); const [page, setPage] = useState(1); const filters = useMemo( () => ({ claimed_end_ms: timeRange.endMs || undefined, claimed_start_ms: timeRange.startMs || undefined, status, ...userIdentityFilterParams(userFilter), }), [status, timeRange, userFilter], ); const { data: claims = emptyClaims, error: claimsError, loading: claimsLoading, reload: reloadClaims, } = usePaginatedQuery({ errorMessage: "加载领取记录失败", fetcher: listRegistrationRewardClaims, filters, page, pageSize, queryKey: ["registration-reward-claims", filters, page], }); const reloadConfig = useCallback(async () => { setConfigLoading(true); try { const [config, groups] = await Promise.all([ getRegistrationRewardConfig(), listResourceGroups({ page: 1, page_size: 100, status: "active" }), ]); setConfig(config); setForm(formFromConfig(config)); setResourceGroups(groups.items || []); } catch (err) { showToast(err.message || "加载注册奖励配置失败", "error"); } finally { setConfigLoading(false); } }, [showToast]); useEffect(() => { void reloadConfig(); }, [reloadConfig]); const openConfigDrawer = useCallback(() => { if (config) { setForm(formFromConfig(config)); } setConfigDrawerOpen(true); }, [config]); const closeConfigDrawer = useCallback(() => { setConfigDrawerOpen(false); if (config) { setForm(formFromConfig(config)); return; } setForm(emptyForm); }, [config]); const changeUserFilter = (value) => { setUserFilter(value); setPage(1); }; const changeStatus = (value) => { setStatus(value); setPage(1); }; const changeTimeRange = (value) => { setTimeRange(value || emptyTimeRange); setPage(1); }; const resetFilters = () => { setUserFilter(emptyUserIdentityFilter); setStatus(""); setTimeRange(emptyTimeRange); setPage(1); }; const submitConfig = async (event) => { event.preventDefault(); if (!abilities.canUpdate) { return; } setConfigSaving(true); try { const saved = await updateRegistrationRewardConfig(payloadFromForm(form)); setConfig(saved); setForm(formFromConfig(saved)); setConfigDrawerOpen(false); showToast("注册奖励配置已保存", "success"); await reloadClaims(); } catch (err) { showToast(err.message || "保存注册奖励配置失败", "error"); } finally { setConfigSaving(false); } }; return { abilities, changeStatus, changeTimeRange, changeUserFilter, claims, claimsError, claimsLoading, closeConfigDrawer, config, configDrawerOpen, configLoading, configSaving, form, openConfigDrawer, page, reloadClaims, reloadConfig, resetFilters, resourceGroups, setForm, setPage, status, submitConfig, timeRange, userFilter, }; } function formFromConfig(config) { return { coinAmount: config.coinAmount ? String(config.coinAmount) : "", dailyLimit: String(config.dailyLimit || 0), enabled: Boolean(config.enabled), resourceGroupId: config.resourceGroupId ? String(config.resourceGroupId) : "", rewardType: config.rewardType || "coin", }; } function payloadFromForm(form) { const rewardType = form.rewardType === "resource_group" ? "resource_group" : "coin"; const coinAmount = Number(form.coinAmount || 0); const resourceGroupId = Number(form.resourceGroupId || 0); const dailyLimit = Number(form.dailyLimit || 0); if (dailyLimit < 0) { throw new Error("每日限制不能小于 0"); } if (rewardType === "coin" && coinAmount <= 0) { throw new Error("金币奖励必须大于 0"); } if (rewardType === "resource_group" && resourceGroupId <= 0) { throw new Error("请选择资源组"); } return { coin_amount: rewardType === "coin" ? coinAmount : 0, daily_limit: dailyLimit, enabled: Boolean(form.enabled), resource_group_id: rewardType === "resource_group" ? resourceGroupId : 0, reward_type: rewardType, }; }