灵仙游戏

This commit is contained in:
170-carry 2026-04-28 18:05:01 +08:00
parent 7aed3ce16d
commit c35af900cd
6 changed files with 188 additions and 4 deletions

View File

@ -91,6 +91,15 @@ export async function saveSpecifiedGiftWeeklyRankConfig(data: Record<string, any
return requestClient.post(`${SPECIFIED_GIFT_WEEKLY_RANK_BASE}/config/save`, data); return requestClient.post(`${SPECIFIED_GIFT_WEEKLY_RANK_BASE}/config/save`, data);
} }
export async function getSpecifiedGiftWeeklyRankCurrentRank(
params: Record<string, any>,
) {
return requestClient.get<Record<string, any>>(
`${SPECIFIED_GIFT_WEEKLY_RANK_BASE}/current-rank`,
{ params },
);
}
export async function pageSpecifiedGiftWeeklyRankSnapshots(params: Record<string, any>) { export async function pageSpecifiedGiftWeeklyRankSnapshots(params: Record<string, any>) {
return requestClient.get<LegacyPageResult<Record<string, any>>>( return requestClient.get<LegacyPageResult<Record<string, any>>>(
`${SPECIFIED_GIFT_WEEKLY_RANK_BASE}/snapshot/page`, `${SPECIFIED_GIFT_WEEKLY_RANK_BASE}/snapshot/page`,

View File

@ -58,6 +58,10 @@ const profileOptions = [
{ label: '正式环境', value: 'PROD' }, { label: '正式环境', value: 'PROD' },
{ label: '测试环境', value: 'TEST' }, { label: '测试环境', value: 'TEST' },
]; ];
const importShowcaseOptions = [
{ label: '默认上架', value: true as any },
{ label: '默认下架', value: false as any },
];
const contextQuery = reactive({ profile: 'PROD', sysOrigin: '' }); const contextQuery = reactive({ profile: 'PROD', sysOrigin: '' });
const providerForm = reactive(createProviderForm()); const providerForm = reactive(createProviderForm());
@ -77,6 +81,7 @@ const totalCatalog = ref(0);
const catalogList = ref<Array<Record<string, any>>>([]); const catalogList = ref<Array<Record<string, any>>>([]);
const selectedCatalogKeys = ref<Array<number>>([]); const selectedCatalogKeys = ref<Array<number>>([]);
const activeProfile = ref('PROD'); const activeProfile = ref('PROD');
const importDefaultShowcase = ref(true as any);
const isCurrentProfileActive = computed( const isCurrentProfileActive = computed(
() => activeProfile.value === contextQuery.profile, () => activeProfile.value === contextQuery.profile,
@ -308,6 +313,7 @@ async function handleImportSelected() {
profile: contextQuery.profile, profile: contextQuery.profile,
sysOrigin: contextQuery.sysOrigin, sysOrigin: contextQuery.sysOrigin,
vendorGameIds: selectedCatalogKeys.value, vendorGameIds: selectedCatalogKeys.value,
defaultShowcase: importDefaultShowcase.value,
}); });
selectedCatalogKeys.value = []; selectedCatalogKeys.value = [];
message.success( message.success(
@ -452,6 +458,11 @@ async function handleImportSelected() {
<Button :loading="catalogFetching" @click="handleFetchCatalog"> <Button :loading="catalogFetching" @click="handleFetchCatalog">
重新获取目录 重新获取目录
</Button> </Button>
<Select
v-model:value="importDefaultShowcase"
:options="importShowcaseOptions"
style="width: 120px"
/>
<Button <Button
type="primary" type="primary"
:loading="catalogImporting" :loading="catalogImporting"

View File

@ -102,6 +102,10 @@ const showcaseOptions = [
{ label: '上架', value: 'true' as any }, { label: '上架', value: 'true' as any },
{ label: '下架', value: 'false' as any }, { label: '下架', value: 'false' as any },
]; ];
const importShowcaseOptions = [
{ label: '默认上架', value: true as any },
{ label: '默认下架', value: false as any },
];
const clientOriginOptions = GAME_CLIENT_ORIGIN_OPTIONS.map((item) => ({ const clientOriginOptions = GAME_CLIENT_ORIGIN_OPTIONS.map((item) => ({
label: item.name, label: item.name,
value: item.value as any, value: item.value as any,
@ -158,6 +162,7 @@ const modalOpen = ref(false);
const gameList = ref<Array<Record<string, any>>>([]); const gameList = ref<Array<Record<string, any>>>([]);
const catalogList = ref<Array<Record<string, any>>>([]); const catalogList = ref<Array<Record<string, any>>>([]);
const selectedCatalogKeys = ref<Array<number>>([]); const selectedCatalogKeys = ref<Array<number>>([]);
const importDefaultShowcase = ref(true as any);
const gameForm = reactive<Record<string, any>>(createGameForm()); const gameForm = reactive<Record<string, any>>(createGameForm());
const providerForm = reactive<Record<string, any>>(createProviderForm()); const providerForm = reactive<Record<string, any>>(createProviderForm());
@ -439,6 +444,7 @@ async function handleImportSelected() {
const result = await importBaishunCatalog({ const result = await importBaishunCatalog({
sysOrigin: contextQuery.sysOrigin, sysOrigin: contextQuery.sysOrigin,
vendorGameIds: selectedCatalogKeys.value, vendorGameIds: selectedCatalogKeys.value,
defaultShowcase: importDefaultShowcase.value,
}); });
selectedCatalogKeys.value = []; selectedCatalogKeys.value = [];
message.success( message.success(
@ -629,6 +635,11 @@ async function handleImportSelected() {
<Button :loading="catalogFetching" @click="handleFetchCatalog"> <Button :loading="catalogFetching" @click="handleFetchCatalog">
重新获取目录 重新获取目录
</Button> </Button>
<Select
v-model:value="importDefaultShowcase"
:options="importShowcaseOptions"
style="width: 120px"
/>
<Button <Button
type="primary" type="primary"
:loading="catalogImporting" :loading="catalogImporting"

View File

@ -40,6 +40,7 @@ function createProviderForm(sysOrigin = '') {
gameListUrl: defaultGameListUrl, gameListUrl: defaultGameListUrl,
profile: 'PROD', profile: 'PROD',
sysOrigin, sysOrigin,
testRoomId: '',
testToken: '', testToken: '',
testUid: '', testUid: '',
}; };
@ -54,6 +55,10 @@ const profileOptions = [
{ label: '正式环境', value: 'PROD' }, { label: '正式环境', value: 'PROD' },
{ label: '测试环境', value: 'TEST' }, { label: '测试环境', value: 'TEST' },
]; ];
const importShowcaseOptions = [
{ label: '默认上架', value: true as any },
{ label: '默认下架', value: false as any },
];
const contextQuery = reactive({ profile: 'PROD', sysOrigin: '' }); const contextQuery = reactive({ profile: 'PROD', sysOrigin: '' });
const providerForm = reactive(createProviderForm()); const providerForm = reactive(createProviderForm());
@ -73,6 +78,7 @@ const totalCatalog = ref(0);
const catalogList = ref<Array<Record<string, any>>>([]); const catalogList = ref<Array<Record<string, any>>>([]);
const selectedCatalogKeys = ref<Array<string>>([]); const selectedCatalogKeys = ref<Array<string>>([]);
const activeProfile = ref('PROD'); const activeProfile = ref('PROD');
const importDefaultShowcase = ref(true as any);
const isCurrentProfileActive = computed( const isCurrentProfileActive = computed(
() => activeProfile.value === contextQuery.profile, () => activeProfile.value === contextQuery.profile,
@ -258,6 +264,7 @@ async function handleImportSelected() {
profile: contextQuery.profile, profile: contextQuery.profile,
sysOrigin: contextQuery.sysOrigin, sysOrigin: contextQuery.sysOrigin,
vendorGameIds: selectedCatalogKeys.value, vendorGameIds: selectedCatalogKeys.value,
defaultShowcase: importDefaultShowcase.value,
}); });
selectedCatalogKeys.value = []; selectedCatalogKeys.value = [];
message.success( message.success(
@ -315,6 +322,9 @@ async function handleImportSelected() {
type="password" type="password"
/> />
</FormItem> </FormItem>
<FormItem label="RoomID">
<Input v-model:value="providerForm.testRoomId" placeholder="可选" />
</FormItem>
</div> </div>
</CollapsePanel> </CollapsePanel>
</Collapse> </Collapse>
@ -357,6 +367,11 @@ async function handleImportSelected() {
<Button :loading="catalogFetching" @click="handleFetchCatalog"> <Button :loading="catalogFetching" @click="handleFetchCatalog">
重新获取目录 重新获取目录
</Button> </Button>
<Select
v-model:value="importDefaultShowcase"
:options="importShowcaseOptions"
style="width: 120px"
/>
<Button <Button
type="primary" type="primary"
:loading="catalogImporting" :loading="catalogImporting"

View File

@ -6,6 +6,7 @@ import { useAccessStore } from '@vben/stores';
import { listGiftBySysOrigin } from '#/api/legacy/gift'; import { listGiftBySysOrigin } from '#/api/legacy/gift';
import { import {
getSpecifiedGiftWeeklyRankCurrentRank,
getSpecifiedGiftWeeklyRankConfig, getSpecifiedGiftWeeklyRankConfig,
pageSpecifiedGiftWeeklyRankConfigs, pageSpecifiedGiftWeeklyRankConfigs,
pageSpecifiedGiftWeeklyRankRewardRecords, pageSpecifiedGiftWeeklyRankRewardRecords,
@ -103,6 +104,26 @@ const rewardLoading = ref(false);
const rewardTotal = ref(0); const rewardTotal = ref(0);
const rewardList = ref<Array<Record<string, any>>>([]); const rewardList = ref<Array<Record<string, any>>>([]);
const rankLoading = ref(false);
const rankList = ref<Array<Record<string, any>>>([]);
const rankMeta = reactive<{
activityStatus: string;
configId: string;
cycleKey: string;
giftConfigs: Array<Record<string, any>>;
periodEndAt: string;
periodStartAt: string;
timezone: string;
}>({
activityStatus: '',
configId: '',
cycleKey: '',
giftConfigs: [],
periodEndAt: '',
periodStartAt: '',
timezone: 'Asia/Riyadh',
});
const form = reactive<Record<string, any>>(createEmptyForm()); const form = reactive<Record<string, any>>(createEmptyForm());
const configQuery = reactive({ const configQuery = reactive({
@ -125,6 +146,10 @@ const rewardQuery = reactive({
status: '', status: '',
}); });
const rankQuery = reactive({
limit: 20,
});
const rewardStatusOptions = [ const rewardStatusOptions = [
{ label: '成功', value: 'SUCCESS' }, { label: '成功', value: 'SUCCESS' },
{ label: '失败', value: 'FAILED' }, { label: '失败', value: 'FAILED' },
@ -155,6 +180,13 @@ const snapshotColumns = [
{ dataIndex: 'period', key: 'period', title: '结算区间', width: 320 }, { dataIndex: 'period', key: 'period', title: '结算区间', width: 320 },
]; ];
const rankColumns = [
{ dataIndex: 'rank', key: 'rank', title: '名次', width: 90 },
{ dataIndex: 'user', key: 'user', title: '用户', width: 280 },
{ dataIndex: 'countryName', key: 'countryName', title: '国家', width: 150 },
{ dataIndex: 'scoreGold', key: 'scoreGold', title: '当前金币积分', width: 160 },
];
const rewardColumns = [ const rewardColumns = [
{ dataIndex: 'cycleKey', key: 'cycleKey', title: '周期', width: 140 }, { dataIndex: 'cycleKey', key: 'cycleKey', title: '周期', width: 140 },
{ dataIndex: 'rank', key: 'rank', title: '名次', width: 90 }, { dataIndex: 'rank', key: 'rank', title: '名次', width: 90 },
@ -301,6 +333,39 @@ async function loadConfigDetail(id: number | string) {
} }
} }
async function loadCurrentRank() {
if (!selectedSysOrigin.value) {
rankList.value = [];
Object.assign(rankMeta, {
activityStatus: '',
configId: '',
cycleKey: '',
giftConfigs: [],
periodEndAt: '',
periodStartAt: '',
timezone: 'Asia/Riyadh',
});
return;
}
rankLoading.value = true;
try {
const result = await getSpecifiedGiftWeeklyRankCurrentRank({
limit: rankQuery.limit,
sysOrigin: selectedSysOrigin.value,
});
rankList.value = Array.isArray(result.records) ? result.records : [];
rankMeta.activityStatus = String(result.activityStatus || '');
rankMeta.configId = String(result.configId || '');
rankMeta.cycleKey = String(result.cycleKey || '');
rankMeta.giftConfigs = Array.isArray(result.giftConfigs) ? result.giftConfigs : [];
rankMeta.periodEndAt = String(result.periodEndAt || '');
rankMeta.periodStartAt = String(result.periodStartAt || '');
rankMeta.timezone = String(result.timezone || 'Asia/Riyadh');
} finally {
rankLoading.value = false;
}
}
async function loadSnapshots(reset = false) { async function loadSnapshots(reset = false) {
if (!selectedSysOrigin.value) { if (!selectedSysOrigin.value) {
snapshotList.value = []; snapshotList.value = [];
@ -346,7 +411,12 @@ async function loadRewardRecords(reset = false) {
} }
async function reloadPageData() { async function reloadPageData() {
await Promise.all([loadConfigList(true), loadSnapshots(true), loadRewardRecords(true)]); await Promise.all([
loadConfigList(true),
loadCurrentRank(),
loadSnapshots(true),
loadRewardRecords(true),
]);
} }
function syncGiftSelection(slot: Record<string, any>, giftId: number | string | undefined) { function syncGiftSelection(slot: Record<string, any>, giftId: number | string | undefined) {
@ -660,6 +730,74 @@ watch(
</div> </div>
</Card> </Card>
<Card :bordered="false" class="mt-16" title="当前排行榜">
<Space class="toolbar" wrap>
<Tag :color="statusColor(rankMeta.activityStatus)">
{{ configStatusTextMap[rankMeta.activityStatus] || rankMeta.activityStatus || '未配置' }}
</Tag>
<Tag color="processing">{{ rankMeta.cycleKey || '-' }}</Tag>
<Tag>{{ rankMeta.timezone || '-' }}</Tag>
<span class="sub-text">
{{ rankMeta.periodStartAt || '-' }} ~ {{ rankMeta.periodEndAt || '-' }}
</span>
<div class="tag-stack">
<Tag
v-for="item in rankMeta.giftConfigs"
:key="`${rankMeta.configId}-${item.giftId}`"
color="gold"
>
{{ item.giftName || item.giftId }}
</Tag>
</div>
<Select
v-model:value="rankQuery.limit"
:options="[
{ label: 'Top 20', value: 20 },
{ label: 'Top 50', value: 50 },
{ label: 'Top 100', value: 100 },
]"
style="width: 120px"
@change="loadCurrentRank"
/>
<Button :loading="rankLoading" type="primary" @click="loadCurrentRank">
刷新排行
</Button>
</Space>
<Table
:columns="rankColumns"
:data-source="rankList"
:loading="rankLoading"
:pagination="false"
row-key="userId"
:scroll="{ x: 680 }"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'user'">
<div class="user-cell">
<Image
v-if="record.userAvatar"
:preview="false"
:src="record.userAvatar"
class="avatar"
/>
<div v-else class="avatar placeholder">U</div>
<div class="user-meta">
<div>{{ record.userNickname || '-' }}</div>
<div class="sub-text">
{{ record.account || record.userId }}
<span v-if="record.countryCode"> · {{ record.countryCode }}</span>
</div>
</div>
</div>
</template>
<template v-else-if="column.key === 'countryName'">
{{ record.countryName || record.countryCode || '-' }}
</template>
</template>
</Table>
</Card>
<Card :bordered="false" class="mt-16" title="上周快照"> <Card :bordered="false" class="mt-16" title="上周快照">
<Space class="toolbar" wrap> <Space class="toolbar" wrap>
<Select <Select

View File

@ -274,7 +274,7 @@ INSERT INTO sys_menu (
SELECT SELECT
1700000256, 1700000256,
(SELECT id FROM sys_menu WHERE alias = 'ResidentActivityManager' LIMIT 1), (SELECT id FROM sys_menu WHERE alias = 'ResidentActivityManager' LIMIT 1),
'指定礼物周榜', '周星',
'resident-activity/week-star/index', 'resident-activity/week-star/index',
2, 2,
'form', 'form',
@ -299,7 +299,7 @@ SET
LIMIT 1 LIMIT 1
) AS resident_menu ) AS resident_menu
), ),
menu_name = '指定礼物周榜', menu_name = '周星',
path = 'resident-activity/week-star/index', path = 'resident-activity/week-star/index',
menu_type = 2, menu_type = 2,
icon = 'form', icon = 'form',
@ -482,7 +482,7 @@ SET menu_name = CONVERT(0xE98280E8AFB7E6B4BBE58AA8E58897E8A1A8 USING utf8mb4)
WHERE alias = 'ResidentInviteList'; WHERE alias = 'ResidentInviteList';
UPDATE sys_menu UPDATE sys_menu
SET menu_name = CONVERT(0xE68C87E5AE9AE7A4BCE789A9E591A8E6A69C USING utf8mb4) SET menu_name = CONVERT(0xE591A8E6989F USING utf8mb4)
WHERE alias = 'ResidentSpecifiedGiftWeeklyRank'; WHERE alias = 'ResidentSpecifiedGiftWeeklyRank';
UPDATE sys_menu UPDATE sys_menu