修复房间奖励

This commit is contained in:
zhx 2026-06-17 10:41:50 +08:00
parent 0be921409d
commit 723258a3aa
8 changed files with 68 additions and 41 deletions

View File

@ -29,6 +29,7 @@ import {
} from "@/shared/ui/AdminListLayout.jsx"; } from "@/shared/ui/AdminListLayout.jsx";
import { DataState } from "@/shared/ui/DataState.jsx"; import { DataState } from "@/shared/ui/DataState.jsx";
import { DataTable } from "@/shared/ui/DataTable.jsx"; import { DataTable } from "@/shared/ui/DataTable.jsx";
import { TimeRangeFilter } from "@/shared/ui/TimeRangeFilter.jsx";
import { TimeText } from "@/shared/ui/TimeText.jsx"; import { TimeText } from "@/shared/ui/TimeText.jsx";
const statusOptions = [ const statusOptions = [
@ -117,8 +118,8 @@ export function AgencyOpeningPage() {
cycleId: cycle.cycleId, cycleId: cycle.cycleId,
title: cycle.title, title: cycle.title,
status: cycle.status || "draft", status: cycle.status || "draft",
startMs: millisToInput(cycle.startMs), startMs: cycle.startMs || "",
endMs: millisToInput(cycle.endMs), endMs: cycle.endMs || "",
minHostCount: String(cycle.minHostCount || 10), minHostCount: String(cycle.minHostCount || 10),
maxAgencyAgeDays: String(cycle.maxAgencyAgeDays || 0), maxAgencyAgeDays: String(cycle.maxAgencyAgeDays || 0),
rewards: (cycle.rewards?.length ? cycle.rewards : emptyForm.rewards).map((reward) => ({ rewards: (cycle.rewards?.length ? cycle.rewards : emptyForm.rewards).map((reward) => ({
@ -136,12 +137,20 @@ export function AgencyOpeningPage() {
if (!abilities.canUpdate && form.cycleId) return; if (!abilities.canUpdate && form.cycleId) return;
setSaving(true); setSaving(true);
try { try {
const startMs = Number(form.startMs || 0);
const endMs = Number(form.endMs || 0);
if (startMs <= 0 || endMs <= 0) {
throw new Error("请选择活动开始和结束时间");
}
if (endMs <= startMs) {
throw new Error("结束时间必须晚于开始时间");
}
const payload = { const payload = {
cycle_id: form.cycleId || undefined, cycle_id: form.cycleId || undefined,
title: form.title.trim(), title: form.title.trim(),
status: form.status, status: form.status,
start_ms: inputToMillis(form.startMs), start_ms: startMs,
end_ms: inputToMillis(form.endMs), end_ms: endMs,
min_host_count: Number(form.minHostCount || 0), min_host_count: Number(form.minHostCount || 0),
max_agency_age_days: Number(form.maxAgencyAgeDays || 0), max_agency_age_days: Number(form.maxAgencyAgeDays || 0),
rewards: normalizeRewardPayload(form.rewards), rewards: normalizeRewardPayload(form.rewards),
@ -421,22 +430,16 @@ export function AgencyOpeningPage() {
</MenuItem> </MenuItem>
))} ))}
</TextField> </TextField>
<TextField <div className="weekly-star-time-range">
label="开始时间" <TimeRangeFilter
required className="weekly-star-time-range__control"
type="datetime-local" label="活动时间"
value={form.startMs} value={{ endMs: form.endMs, startMs: form.startMs }}
onChange={(event) => setForm({ ...form, startMs: event.target.value })} onChange={(range) =>
InputLabelProps={{ shrink: true }} setForm({ ...form, endMs: range.endMs, startMs: range.startMs })
/> }
<TextField />
label="结束时间" </div>
required
type="datetime-local"
value={form.endMs}
onChange={(event) => setForm({ ...form, endMs: event.target.value })}
InputLabelProps={{ shrink: true }}
/>
<TextField <TextField
label="主播数必须大于" label="主播数必须大于"
required required
@ -548,16 +551,6 @@ function applicationStatusLabel(status) {
); );
} }
function millisToInput(ms) {
if (!ms) return "";
const local = new Date(ms - new Date(ms).getTimezoneOffset() * 60000);
return local.toISOString().slice(0, 16);
}
function inputToMillis(value) {
return value ? new Date(value).getTime() : 0;
}
function normalizeRewardPayload(rewards) { function normalizeRewardPayload(rewards) {
const payload = (rewards || []).map((reward, index) => { const payload = (rewards || []).map((reward, index) => {
const thresholdCoinSpent = Number(reward.thresholdCoinSpent || 0); const thresholdCoinSpent = Number(reward.thresholdCoinSpent || 0);

View File

@ -40,6 +40,7 @@ export interface RoomTurnoverRewardSettlementDto {
settlementId: string; settlementId: string;
roomId: string; roomId: string;
ownerUserId: string; ownerUserId: string;
ownerUser?: Record<string, unknown>;
periodStartMs: number; periodStartMs: number;
periodEndMs: number; periodEndMs: number;
coinSpent: number; coinSpent: number;
@ -59,6 +60,8 @@ export interface RoomTurnoverRewardSettlementDto {
export interface RoomTurnoverRewardSettlementQuery extends PageQuery { export interface RoomTurnoverRewardSettlementQuery extends PageQuery {
status?: string; status?: string;
keyword?: string; keyword?: string;
owner_user_keyword?: string;
owner_user_id?: string;
} }
type RawConfig = RoomTurnoverRewardConfigDto & Record<string, unknown>; type RawConfig = RoomTurnoverRewardConfigDto & Record<string, unknown>;
@ -141,6 +144,7 @@ function normalizeSettlement(item: RawSettlement): RoomTurnoverRewardSettlementD
settlementId: stringValue(item.settlementId ?? item.settlement_id), settlementId: stringValue(item.settlementId ?? item.settlement_id),
roomId: stringValue(item.roomId ?? item.room_id), roomId: stringValue(item.roomId ?? item.room_id),
ownerUserId: stringValue(item.ownerUserId ?? item.owner_user_id), ownerUserId: stringValue(item.ownerUserId ?? item.owner_user_id),
ownerUser: objectValue(item.ownerUser ?? item.owner_user),
periodStartMs: numberValue(item.periodStartMs ?? item.period_start_ms), periodStartMs: numberValue(item.periodStartMs ?? item.period_start_ms),
periodEndMs: numberValue(item.periodEndMs ?? item.period_end_ms), periodEndMs: numberValue(item.periodEndMs ?? item.period_end_ms),
coinSpent: numberValue(item.coinSpent ?? item.coin_spent), coinSpent: numberValue(item.coinSpent ?? item.coin_spent),
@ -166,3 +170,7 @@ function numberValue(value: unknown) {
const number = Number(value || 0); const number = Number(value || 0);
return Number.isFinite(number) ? number : 0; return Number.isFinite(number) ? number : 0;
} }
function objectValue(value: unknown) {
return value && typeof value === "object" && !Array.isArray(value) ? (value as Record<string, unknown>) : undefined;
}

View File

@ -29,9 +29,13 @@ export function useRoomTurnoverRewardPage() {
const [configSaving, setConfigSaving] = useState(false); const [configSaving, setConfigSaving] = useState(false);
const [retryingSettlementId, setRetryingSettlementId] = useState(""); const [retryingSettlementId, setRetryingSettlementId] = useState("");
const [query, setQuery] = useState(""); const [query, setQuery] = useState("");
const [ownerUserKeyword, setOwnerUserKeyword] = useState("");
const [status, setStatus] = useState(""); const [status, setStatus] = useState("");
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const filters = useMemo(() => ({ keyword: query, status }), [query, status]); const filters = useMemo(
() => ({ keyword: query, owner_user_keyword: ownerUserKeyword, status }),
[ownerUserKeyword, query, status],
);
const { const {
data: settlements = emptySettlements, data: settlements = emptySettlements,
error: settlementsError, error: settlementsError,
@ -83,6 +87,11 @@ export function useRoomTurnoverRewardPage() {
setPage(1); setPage(1);
}; };
const changeOwnerUserKeyword = (value) => {
setOwnerUserKeyword(value);
setPage(1);
};
const submitConfig = async (event) => { const submitConfig = async (event) => {
event.preventDefault(); event.preventDefault();
if (!abilities.canUpdate) { if (!abilities.canUpdate) {
@ -129,6 +138,7 @@ export function useRoomTurnoverRewardPage() {
return { return {
abilities, abilities,
changeOwnerUserKeyword,
changeQuery, changeQuery,
changeStatus, changeStatus,
closeConfigDrawer, closeConfigDrawer,
@ -138,6 +148,7 @@ export function useRoomTurnoverRewardPage() {
configSaving, configSaving,
form, form,
openConfigDrawer, openConfigDrawer,
ownerUserKeyword,
page, page,
query, query,
reloadConfig, reloadConfig,

View File

@ -4,6 +4,7 @@ import { RoomTurnoverRewardConfigSummary } from "@/features/room-turnover-reward
import { useRoomTurnoverRewardPage } from "@/features/room-turnover-reward/hooks/useRoomTurnoverRewardPage.js"; import { useRoomTurnoverRewardPage } from "@/features/room-turnover-reward/hooks/useRoomTurnoverRewardPage.js";
import styles from "@/features/room-turnover-reward/room-turnover-reward.module.css"; import styles from "@/features/room-turnover-reward/room-turnover-reward.module.css";
import { AdminActionIconButton, AdminListBody, AdminListPage } from "@/shared/ui/AdminListLayout.jsx"; import { AdminActionIconButton, AdminListBody, AdminListPage } from "@/shared/ui/AdminListLayout.jsx";
import { AdminUserIdentity } from "@/shared/ui/AdminUserIdentity.jsx";
import { DataState } from "@/shared/ui/DataState.jsx"; import { DataState } from "@/shared/ui/DataState.jsx";
import { DataTable } from "@/shared/ui/DataTable.jsx"; import { DataTable } from "@/shared/ui/DataTable.jsx";
import { createOptionsColumnFilter, createTextColumnFilter } from "@/shared/ui/tableFilters.js"; import { createOptionsColumnFilter, createTextColumnFilter } from "@/shared/ui/tableFilters.js";
@ -23,10 +24,15 @@ const columnsBase = [
render: (item) => ( render: (item) => (
<div className={styles.stack}> <div className={styles.stack}>
<span className={styles.name}>{item.roomId || "-"}</span> <span className={styles.name}>{item.roomId || "-"}</span>
<span className={styles.meta}>房主 {item.ownerUserId || "-"}</span>
</div> </div>
), ),
}, },
{
key: "owner",
label: "房主信息",
width: "minmax(240px, 0.95fr)",
render: (item) => <OwnerUser item={item} />,
},
{ {
key: "period", key: "period",
label: "周期", label: "周期",
@ -122,6 +128,16 @@ export function RoomTurnoverRewardPage() {
}), }),
}; };
} }
if (column.key === "owner") {
return {
...column,
filter: createTextColumnFilter({
placeholder: "搜索房主 ID、短号、名称",
value: page.ownerUserKeyword,
onChange: page.changeOwnerUserKeyword,
}),
};
}
if (column.key === "status") { if (column.key === "status") {
return { return {
...column, ...column,
@ -155,7 +171,7 @@ export function RoomTurnoverRewardPage() {
retrySettlement: page.retrySettlement, retrySettlement: page.retrySettlement,
}} }}
items={page.settlements.items || []} items={page.settlements.items || []}
minWidth="1480px" minWidth="1700px"
pagination={ pagination={
total > 0 total > 0
? { ? {
@ -184,6 +200,11 @@ export function RoomTurnoverRewardPage() {
); );
} }
function OwnerUser({ item }) {
const owner = item.ownerUser || {};
return <AdminUserIdentity openInAppUserDetail user={{ ...owner, userId: owner.userId || item.ownerUserId }} />;
}
function settlementStatusBadge(status) { function settlementStatusBadge(status) {
const label = settlementStatusOptions.find(([value]) => value === status)?.[1] || status || "-"; const label = settlementStatusOptions.find(([value]) => value === status)?.[1] || status || "-";
const className = const className =

View File

@ -29,7 +29,6 @@ function emptyForm() {
minRobotCount: "6", minRobotCount: "6",
normalGiftIntervalMs: "10000", normalGiftIntervalMs: "10000",
ownerRobotUserId: "", ownerRobotUserId: "",
roomName: "",
visibleRegionId: 0, visibleRegionId: 0,
}; };
} }

View File

@ -153,11 +153,6 @@ export function RoomRobotPage() {
onClose={page.closeAction} onClose={page.closeAction}
onSubmit={page.submitCreate} onSubmit={page.submitCreate}
> >
<TextField
label="房间名称"
value={page.form.roomName}
onChange={(event) => page.setForm({ ...page.form, roomName: event.target.value })}
/>
<Autocomplete <Autocomplete
getOptionLabel={robotOptionLabel} getOptionLabel={robotOptionLabel}
isOptionEqualToValue={(option, value) => option.userId === value.userId} isOptionEqualToValue={(option, value) => option.userId === value.userId}

View File

@ -83,7 +83,6 @@ export const robotRoomCreateSchema = z
minRobotCount: z.coerce.number().int().min(1, "机器人数量范围不正确"), minRobotCount: z.coerce.number().int().min(1, "机器人数量范围不正确"),
normalGiftIntervalMs: z.coerce.number().int().min(1000, "普通礼物频次不正确"), normalGiftIntervalMs: z.coerce.number().int().min(1000, "普通礼物频次不正确"),
ownerRobotUserId: z.coerce.number().int().positive("请选择房主机器人"), ownerRobotUserId: z.coerce.number().int().positive("请选择房主机器人"),
roomName: z.string().trim().max(128, "房间名称不能超过 128 个字符").optional().default(""),
visibleRegionId: z.coerce.number().int().min(0, "区域 ID 不正确").default(0), visibleRegionId: z.coerce.number().int().min(0, "区域 ID 不正确").default(0),
}) })
.refine((value) => value.maxRobotCount >= value.minRobotCount, { .refine((value) => value.maxRobotCount >= value.minRobotCount, {

View File

@ -1048,10 +1048,11 @@
.weekly-star-time-range { .weekly-star-time-range {
display: flex; display: flex;
align-items: center; align-items: center;
grid-column: 1 / -1;
} }
.weekly-star-time-range__control { .weekly-star-time-range__control {
width: min(520px, 100%); width: min(680px, 100%) !important;
} }
.weekly-star-config-card-grid { .weekly-star-config-card-grid {