模拟飘屏

This commit is contained in:
zhx 2026-05-25 21:03:07 +08:00
parent 6e516e95f2
commit bdd3a99bba
5 changed files with 355 additions and 4 deletions

View File

@ -0,0 +1,8 @@
import { requestClient } from '#/api/request';
export async function simulateRegionBroadcast(data: Record<string, any>) {
return requestClient.post<Record<string, any>>(
'/go/operate/region-broadcast/simulate',
data,
);
}

View File

@ -65,6 +65,12 @@ const routes: RouteRecordRaw[] = [
component: () => import('#/views/operate/tools-other.vue'),
meta: { title: '小工具' },
},
{
name: 'OperateRegionBroadcast',
path: 'region-broadcast',
component: () => import('#/views/operate/region-broadcast.vue'),
meta: { title: '区域飘屏' },
},
{
name: 'OperateAbnormalLog',
path: 'abnormal/log',

View File

@ -226,6 +226,21 @@ function formatMoney(value?: number | string) {
return `$${amount.toFixed(2)}`;
}
function shouldShowAgentSalary(record: Record<string, any>) {
if (record.agentMember !== undefined && record.agentMember !== null) {
return record.agentMember === true;
}
return (
!!record.userId &&
!!record.teamOwnId &&
String(record.userId) === String(record.teamOwnId)
);
}
function formatAgentMoney(record: Record<string, any>, key: string) {
return shouldShowAgentSalary(record) ? formatMoney(record[key]) : '-';
}
function formatInteger(value?: number | string) {
if (value === undefined || value === null || value === '') {
return '-';
@ -493,7 +508,7 @@ void loadCountries();
{{ formatMoney(record.expectedMemberSalary) }}
</template>
<template v-else-if="column.key === 'expectedAgentSalary'">
{{ formatMoney(record.expectedAgentSalary) }}
{{ formatAgentMoney(record, 'expectedAgentSalary') }}
</template>
<template v-else-if="column.key === 'payableMemberSalary'">
<span
@ -506,7 +521,7 @@ void loadCountries();
<span
:class="{ 'payable-salary-negative': Number(record.payableAgentSalary || 0) < 0 }"
>
{{ formatMoney(record.payableAgentSalary) }}
{{ formatAgentMoney(record, 'payableAgentSalary') }}
</span>
</template>
<template v-else-if="column.key === 'payableSalary'">
@ -529,9 +544,9 @@ void loadCountries();
</span>
</template>
<template v-else-if="column.key === 'agentIssuedSalary'">
<span>{{ formatMoney(record.agentIssuedSalary) }}</span>
<span>{{ formatAgentMoney(record, 'agentIssuedSalary') }}</span>
<span
v-if="Number(record.agentOverIssuedSalary || 0) > 0"
v-if="shouldShowAgentSalary(record) && Number(record.agentOverIssuedSalary || 0) > 0"
class="over-issued-salary"
>
({{ formatMoney(record.agentOverIssuedSalary) }})

View File

@ -0,0 +1,247 @@
<script lang="ts" setup>
import { computed, reactive, ref, watch } from 'vue';
import { Page } from '@vben/common-ui';
import { useAccessStore } from '@vben/stores';
import { simulateRegionBroadcast } from '#/api/legacy/region-broadcast';
import { getAllowedSysOrigins } from '#/views/system/shared';
import SystemRegionSelect from './components/system-region-select.vue';
import {
Button,
Card,
Form,
FormItem,
Input,
Select,
Space,
TextArea,
message,
} from 'antdv-next';
defineOptions({ name: 'OperateRegionBroadcast' });
const accessStore = useAccessStore();
const sysOriginOptions = computed(() => {
const options = getAllowedSysOrigins(accessStore.accessCodes || []);
return (options.length > 0 ? options : getAllowedSysOrigins([])).map((item) => ({
label: item.label,
value: item.value,
}));
});
const kindOptions = [
{ label: '幸运礼物中奖飘屏', value: 'LUCKY_GIFT' },
{ label: '游戏盈利飘屏', value: 'GAME_WIN' },
];
const form = reactive<Record<string, any>>({
acceptUserId: '',
betAmount: '100',
gameId: 'admin-sim-game',
gameRoundId: '',
gameUrl: '',
giftCandy: '100',
giftId: '',
giftQuantity: '1',
kind: 'LUCKY_GIFT',
multiple: '10',
providerOrderId: '',
regionCode: '',
regionId: '',
roomId: '',
senderUserId: '',
sysOrigin: '',
winAmount: '1000',
});
const sending = ref(false);
const resultText = ref('');
const isLuckyGift = computed(() => form.kind === 'LUCKY_GIFT');
watch(
sysOriginOptions,
(options) => {
if (!form.sysOrigin && options.length > 0) {
form.sysOrigin = String(options[0]?.value || '');
}
},
{ immediate: true },
);
watch(
() => form.sysOrigin,
() => {
form.regionId = '';
form.regionCode = '';
},
);
function handleRegionChange(_value: any, item: Record<string, any> | null) {
form.regionCode = item?.regionCode || item?.code || '';
}
function normalizePayload() {
return {
acceptUserId: String(form.acceptUserId || '').trim(),
betAmount: String(form.betAmount || '').trim(),
gameId: String(form.gameId || '').trim(),
gameRoundId: String(form.gameRoundId || '').trim(),
gameUrl: String(form.gameUrl || '').trim(),
giftCandy: String(form.giftCandy || '').trim(),
giftId: String(form.giftId || '').trim(),
giftQuantity: String(form.giftQuantity || '').trim(),
kind: form.kind,
multiple: String(form.multiple || '').trim(),
providerOrderId: String(form.providerOrderId || '').trim(),
regionCode: String(form.regionCode || '').trim(),
roomId: String(form.roomId || '').trim(),
senderUserId: String(form.senderUserId || '').trim(),
sysOrigin: String(form.sysOrigin || '').trim(),
winAmount: String(form.winAmount || '').trim(),
};
}
function validatePayload(payload: Record<string, any>) {
if (!payload.sysOrigin) {
return '请选择系统来源';
}
if (!payload.regionCode) {
return '请选择区域';
}
if (!payload.senderUserId) {
return '请输入发送用户ID';
}
if (!payload.roomId) {
return '请输入房间ID';
}
if (!payload.multiple || Number(payload.multiple) < 10) {
return '中奖倍数必须大于等于10';
}
if (!payload.winAmount || Number(payload.winAmount) <= 0) {
return '中奖金额必须大于0';
}
return '';
}
async function handleSend() {
const payload = normalizePayload();
const error = validatePayload(payload);
if (error) {
message.warning(error);
return;
}
sending.value = true;
try {
const result = await simulateRegionBroadcast(payload);
resultText.value = JSON.stringify(result || {}, null, 2);
message.success('区域飘屏已发送');
} finally {
sending.value = false;
}
}
</script>
<template>
<Page title="区域飘屏">
<Card>
<Form layout="vertical">
<div class="form-grid">
<FormItem label="系统来源" required>
<Select
v-model:value="form.sysOrigin"
:options="sysOriginOptions"
placeholder="请选择系统来源"
/>
</FormItem>
<FormItem label="区域" required>
<SystemRegionSelect
v-model:value="form.regionId"
:sys-origin="form.sysOrigin"
placeholder="请选择区域"
@change="handleRegionChange"
/>
</FormItem>
<FormItem label="飘屏类型" required>
<Select v-model:value="form.kind" :options="kindOptions" />
</FormItem>
<FormItem label="发送用户ID" required>
<Input v-model:value="form.senderUserId" placeholder="用户长ID" />
</FormItem>
<FormItem label="房间ID" required>
<Input v-model:value="form.roomId" placeholder="房间ID" />
</FormItem>
<FormItem label="中奖倍数" required>
<Input v-model:value="form.multiple" placeholder="10" />
</FormItem>
<FormItem label="中奖金额" required>
<Input v-model:value="form.winAmount" placeholder="金币数量" />
</FormItem>
<template v-if="isLuckyGift">
<FormItem label="收礼用户ID">
<Input v-model:value="form.acceptUserId" placeholder="可选" />
</FormItem>
<FormItem label="礼物ID">
<Input v-model:value="form.giftId" placeholder="可选" />
</FormItem>
<FormItem label="礼物单价">
<Input v-model:value="form.giftCandy" />
</FormItem>
<FormItem label="礼物数量">
<Input v-model:value="form.giftQuantity" />
</FormItem>
<FormItem label="业务单号">
<Input v-model:value="form.providerOrderId" placeholder="可选" />
</FormItem>
</template>
<template v-else>
<FormItem label="游戏ID">
<Input v-model:value="form.gameId" />
</FormItem>
<FormItem label="游戏局号">
<Input v-model:value="form.gameRoundId" placeholder="可选" />
</FormItem>
<FormItem label="投注金额">
<Input v-model:value="form.betAmount" />
</FormItem>
<FormItem label="游戏封面">
<Input v-model:value="form.gameUrl" placeholder="可选" />
</FormItem>
</template>
</div>
<Space>
<Button :loading="sending" type="primary" @click="handleSend">
发送模拟飘屏
</Button>
</Space>
</Form>
</Card>
<Card class="result-card" title="发送结果">
<TextArea
v-model:value="resultText"
:auto-size="{ minRows: 8, maxRows: 16 }"
readonly
placeholder="发送成功后显示服务端返回内容"
/>
</Card>
</Page>
</template>
<style scoped>
.form-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 12px 16px;
}
.result-card {
margin-top: 16px;
}
</style>

View File

@ -0,0 +1,75 @@
SET NAMES utf8mb4;
SET @operate_manager_id := (
SELECT id
FROM sys_menu
WHERE alias = 'OperateManager'
LIMIT 1
);
INSERT INTO sys_menu (
id,
parent_id,
menu_name,
path,
menu_type,
icon,
create_user,
update_user,
sort,
status,
router,
alias
)
SELECT
menu_id.next_id,
@operate_manager_id,
'区域飘屏',
'operate/region-broadcast',
2,
'lucide:radio-tower',
'0',
'0',
18,
0,
'region-broadcast',
'OperateRegionBroadcast'
FROM (
SELECT COALESCE(MAX(id), 1700000000) + 1 AS next_id
FROM sys_menu
) AS menu_id
WHERE @operate_manager_id IS NOT NULL
AND NOT EXISTS (
SELECT 1 FROM sys_menu WHERE alias = 'OperateRegionBroadcast'
);
UPDATE sys_menu
SET
parent_id = @operate_manager_id,
menu_name = '区域飘屏',
path = 'operate/region-broadcast',
menu_type = 2,
icon = 'lucide:radio-tower',
update_user = '0',
sort = 18,
status = 0,
router = 'region-broadcast'
WHERE alias = 'OperateRegionBroadcast'
AND @operate_manager_id IS NOT NULL;
INSERT INTO sys_role_menu (role_id, menu_id)
SELECT DISTINCT
role_menu.role_id,
target_menu.id
FROM sys_role_menu AS role_menu
JOIN sys_menu AS source_menu
ON source_menu.id = role_menu.menu_id
AND source_menu.alias = 'OperateManager'
JOIN sys_menu AS target_menu
ON target_menu.alias = 'OperateRegionBroadcast'
WHERE NOT EXISTS (
SELECT 1
FROM sys_role_menu AS existing_role_menu
WHERE existing_role_menu.role_id = role_menu.role_id
AND existing_role_menu.menu_id = target_menu.id
);