修复房间奖励

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

View File

@ -40,6 +40,7 @@ export interface RoomTurnoverRewardSettlementDto {
settlementId: string;
roomId: string;
ownerUserId: string;
ownerUser?: Record<string, unknown>;
periodStartMs: number;
periodEndMs: number;
coinSpent: number;
@ -59,6 +60,8 @@ export interface RoomTurnoverRewardSettlementDto {
export interface RoomTurnoverRewardSettlementQuery extends PageQuery {
status?: string;
keyword?: string;
owner_user_keyword?: string;
owner_user_id?: string;
}
type RawConfig = RoomTurnoverRewardConfigDto & Record<string, unknown>;
@ -141,6 +144,7 @@ function normalizeSettlement(item: RawSettlement): RoomTurnoverRewardSettlementD
settlementId: stringValue(item.settlementId ?? item.settlement_id),
roomId: stringValue(item.roomId ?? item.room_id),
ownerUserId: stringValue(item.ownerUserId ?? item.owner_user_id),
ownerUser: objectValue(item.ownerUser ?? item.owner_user),
periodStartMs: numberValue(item.periodStartMs ?? item.period_start_ms),
periodEndMs: numberValue(item.periodEndMs ?? item.period_end_ms),
coinSpent: numberValue(item.coinSpent ?? item.coin_spent),
@ -166,3 +170,7 @@ function numberValue(value: unknown) {
const number = Number(value || 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 [retryingSettlementId, setRetryingSettlementId] = useState("");
const [query, setQuery] = useState("");
const [ownerUserKeyword, setOwnerUserKeyword] = useState("");
const [status, setStatus] = useState("");
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 {
data: settlements = emptySettlements,
error: settlementsError,
@ -83,6 +87,11 @@ export function useRoomTurnoverRewardPage() {
setPage(1);
};
const changeOwnerUserKeyword = (value) => {
setOwnerUserKeyword(value);
setPage(1);
};
const submitConfig = async (event) => {
event.preventDefault();
if (!abilities.canUpdate) {
@ -129,6 +138,7 @@ export function useRoomTurnoverRewardPage() {
return {
abilities,
changeOwnerUserKeyword,
changeQuery,
changeStatus,
closeConfigDrawer,
@ -138,6 +148,7 @@ export function useRoomTurnoverRewardPage() {
configSaving,
form,
openConfigDrawer,
ownerUserKeyword,
page,
query,
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 styles from "@/features/room-turnover-reward/room-turnover-reward.module.css";
import { AdminActionIconButton, AdminListBody, AdminListPage } from "@/shared/ui/AdminListLayout.jsx";
import { AdminUserIdentity } from "@/shared/ui/AdminUserIdentity.jsx";
import { DataState } from "@/shared/ui/DataState.jsx";
import { DataTable } from "@/shared/ui/DataTable.jsx";
import { createOptionsColumnFilter, createTextColumnFilter } from "@/shared/ui/tableFilters.js";
@ -23,10 +24,15 @@ const columnsBase = [
render: (item) => (
<div className={styles.stack}>
<span className={styles.name}>{item.roomId || "-"}</span>
<span className={styles.meta}>房主 {item.ownerUserId || "-"}</span>
</div>
),
},
{
key: "owner",
label: "房主信息",
width: "minmax(240px, 0.95fr)",
render: (item) => <OwnerUser item={item} />,
},
{
key: "period",
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") {
return {
...column,
@ -155,7 +171,7 @@ export function RoomTurnoverRewardPage() {
retrySettlement: page.retrySettlement,
}}
items={page.settlements.items || []}
minWidth="1480px"
minWidth="1700px"
pagination={
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) {
const label = settlementStatusOptions.find(([value]) => value === status)?.[1] || status || "-";
const className =

View File

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

View File

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

View File

@ -83,7 +83,6 @@ export const robotRoomCreateSchema = z
minRobotCount: z.coerce.number().int().min(1, "机器人数量范围不正确"),
normalGiftIntervalMs: z.coerce.number().int().min(1000, "普通礼物频次不正确"),
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),
})
.refine((value) => value.maxRobotCount >= value.minRobotCount, {

View File

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