feat: add register reward daily records
This commit is contained in:
parent
ca35226a1c
commit
474caa56e0
@ -45,6 +45,15 @@ export async function resetResidentRegisterRewardConfig(sysOrigin: string) {
|
||||
});
|
||||
}
|
||||
|
||||
export async function pageResidentRegisterRewardDailyRecords(
|
||||
params: Record<string, any>,
|
||||
) {
|
||||
return requestClient.get<Record<string, any>>(
|
||||
'/go/resident-activity/register-reward/daily-record/page',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
export async function pageSpecifiedGiftWeeklyRankConfigs(params: Record<string, any>) {
|
||||
return requestClient.get<LegacyPageResult<Record<string, any>>>(
|
||||
`${SPECIFIED_GIFT_WEEKLY_RANK_BASE}/config/page`,
|
||||
|
||||
@ -6,21 +6,26 @@ import { useAccessStore } from '@vben/stores';
|
||||
|
||||
import {
|
||||
getResidentRegisterRewardConfig,
|
||||
pageResidentRegisterRewardDailyRecords,
|
||||
resetResidentRegisterRewardConfig,
|
||||
saveResidentRegisterRewardConfig,
|
||||
} from '#/api/legacy/resident-activity';
|
||||
import ActivityResourceGroupSelectDrawer from '#/views/props/components/activity-resource-group-select-drawer.vue';
|
||||
import UserProfileLink from '#/views/operate/components/user-profile-link.vue';
|
||||
import { getAllowedSysOrigins } from '#/views/system/shared';
|
||||
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
DatePicker,
|
||||
InputNumber,
|
||||
Modal,
|
||||
Pagination,
|
||||
Select,
|
||||
Space,
|
||||
Spin,
|
||||
Switch,
|
||||
Table,
|
||||
Tag,
|
||||
message,
|
||||
} from 'antdv-next';
|
||||
@ -35,10 +40,14 @@ const sysOriginOptions = computed(() => {
|
||||
|
||||
const loading = ref(false);
|
||||
const saving = ref(false);
|
||||
const recordLoading = ref(false);
|
||||
const recordTotal = ref(0);
|
||||
const groupPickerOpen = ref(false);
|
||||
const recordList = ref<Array<Record<string, any>>>([]);
|
||||
|
||||
const form = reactive<Record<string, any>>({
|
||||
configured: false,
|
||||
dailyLimit: 0,
|
||||
enabled: false,
|
||||
goldAmount: 0,
|
||||
rewardGroupId: null,
|
||||
@ -46,6 +55,22 @@ const form = reactive<Record<string, any>>({
|
||||
sysOrigin: '',
|
||||
});
|
||||
|
||||
const recordQuery = reactive<Record<string, any>>({
|
||||
claimDate: '',
|
||||
cursor: 1,
|
||||
limit: 20,
|
||||
});
|
||||
|
||||
const recordSummary = reactive<Record<string, any>>({
|
||||
dailyLimit: 0,
|
||||
date: '',
|
||||
issuedCount: 0,
|
||||
limitEnabled: false,
|
||||
occupiedCount: 0,
|
||||
remainingCount: 0,
|
||||
successCount: 0,
|
||||
});
|
||||
|
||||
const statusMeta = computed(() => {
|
||||
if (!form.configured) {
|
||||
return {
|
||||
@ -57,7 +82,10 @@ const statusMeta = computed(() => {
|
||||
if (form.enabled) {
|
||||
return {
|
||||
color: 'success',
|
||||
description: '当前已启用,注册后会按下面配置发金币和/或奖励组。',
|
||||
description:
|
||||
form.dailyLimit > 0
|
||||
? `当前已启用,每日最多发放 ${form.dailyLimit} 份注册奖励。`
|
||||
: '当前已启用,注册后会按下面配置发金币和/或奖励组。',
|
||||
text: '已启用',
|
||||
};
|
||||
}
|
||||
@ -68,9 +96,20 @@ const statusMeta = computed(() => {
|
||||
};
|
||||
});
|
||||
|
||||
const recordColumns = [
|
||||
{ dataIndex: 'user', key: 'user', title: '领取用户', width: 280 },
|
||||
{ dataIndex: 'claimResult', key: 'claimResult', title: '领取结果', width: 220 },
|
||||
{ dataIndex: 'status', key: 'status', title: '处理状态', width: 120 },
|
||||
{ dataIndex: 'rewardPayload', key: 'rewardPayload', title: '奖励内容', width: 220 },
|
||||
{ dataIndex: 'claimDate', key: 'claimDate', title: '领取日期', width: 120 },
|
||||
{ dataIndex: 'createTime', key: 'createTime', title: '触发时间', width: 180 },
|
||||
{ dataIndex: 'errorMessage', key: 'errorMessage', title: '备注 / 失败原因', width: 260 },
|
||||
];
|
||||
|
||||
function normalizeConfig(result: Record<string, any> | null | undefined) {
|
||||
const value = result ?? {};
|
||||
form.configured = Boolean(value.configured);
|
||||
form.dailyLimit = Number(value.dailyLimit || 0);
|
||||
form.enabled = Boolean(value.enabled);
|
||||
form.goldAmount = Number(value.goldAmount || 0);
|
||||
form.rewardGroupId = value.rewardGroupId ?? null;
|
||||
@ -78,7 +117,46 @@ function normalizeConfig(result: Record<string, any> | null | undefined) {
|
||||
form.sysOrigin = String(value.sysOrigin || form.sysOrigin || sysOriginOptions.value[0]?.value || '');
|
||||
}
|
||||
|
||||
async function loadData() {
|
||||
function syncRecordSummary(result: Record<string, any> | null | undefined) {
|
||||
const value = result ?? {};
|
||||
recordSummary.date = String(value.date || recordSummary.date || '');
|
||||
recordSummary.dailyLimit = Number(value.dailyLimit || 0);
|
||||
recordSummary.limitEnabled = Boolean(value.limitEnabled);
|
||||
recordSummary.occupiedCount = Number(value.occupiedCount || 0);
|
||||
recordSummary.issuedCount = Number(value.issuedCount || 0);
|
||||
recordSummary.successCount = Number(value.successCount || 0);
|
||||
recordSummary.remainingCount = Number(value.remainingCount || 0);
|
||||
}
|
||||
|
||||
function claimResultColor(result: string) {
|
||||
switch (String(result || '').toUpperCase()) {
|
||||
case 'REWARDED':
|
||||
return 'success';
|
||||
case 'QUOTA_EXHAUSTED':
|
||||
return 'warning';
|
||||
case 'FAILED':
|
||||
return 'error';
|
||||
case 'SKIPPED':
|
||||
return 'default';
|
||||
default:
|
||||
return 'processing';
|
||||
}
|
||||
}
|
||||
|
||||
function processStatusColor(status: string) {
|
||||
switch (String(status || '').toUpperCase()) {
|
||||
case 'SUCCESS':
|
||||
return 'success';
|
||||
case 'FAILED':
|
||||
return 'error';
|
||||
case 'SKIPPED':
|
||||
return 'default';
|
||||
default:
|
||||
return 'processing';
|
||||
}
|
||||
}
|
||||
|
||||
async function loadConfig() {
|
||||
if (!form.sysOrigin) {
|
||||
return;
|
||||
}
|
||||
@ -91,6 +169,36 @@ async function loadData() {
|
||||
}
|
||||
}
|
||||
|
||||
async function loadDailyRecords(reset = false) {
|
||||
if (!form.sysOrigin) {
|
||||
recordList.value = [];
|
||||
recordTotal.value = 0;
|
||||
return;
|
||||
}
|
||||
if (reset) {
|
||||
recordQuery.cursor = 1;
|
||||
}
|
||||
recordLoading.value = true;
|
||||
try {
|
||||
const result = await pageResidentRegisterRewardDailyRecords({
|
||||
...recordQuery,
|
||||
sysOrigin: form.sysOrigin,
|
||||
});
|
||||
recordList.value = Array.isArray(result.records) ? result.records : [];
|
||||
recordTotal.value = Number(result.total || 0);
|
||||
syncRecordSummary(result);
|
||||
if (!recordQuery.claimDate && result.date) {
|
||||
recordQuery.claimDate = String(result.date);
|
||||
}
|
||||
} finally {
|
||||
recordLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function loadData() {
|
||||
await Promise.all([loadConfig(), loadDailyRecords(true)]);
|
||||
}
|
||||
|
||||
watch(
|
||||
sysOriginOptions,
|
||||
(options) => {
|
||||
@ -130,6 +238,7 @@ async function submitForm() {
|
||||
saving.value = true;
|
||||
try {
|
||||
await saveResidentRegisterRewardConfig({
|
||||
dailyLimit: Number(form.dailyLimit || 0),
|
||||
enabled: Boolean(form.enabled),
|
||||
goldAmount: Number(form.goldAmount || 0),
|
||||
rewardGroupId: form.rewardGroupId ?? null,
|
||||
@ -167,6 +276,16 @@ function handleReset() {
|
||||
content: '清空后会删除当前系统的后台配置,普通注册成功后不再发放注册奖励。',
|
||||
});
|
||||
}
|
||||
|
||||
function handleRecordSearch() {
|
||||
void loadDailyRecords(true);
|
||||
}
|
||||
|
||||
function handleRecordPageChange(page: number, pageSize: number) {
|
||||
recordQuery.cursor = page;
|
||||
recordQuery.limit = pageSize;
|
||||
void loadDailyRecords();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -206,6 +325,19 @@ function handleReset() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="field-label">每日限制份数</div>
|
||||
<InputNumber
|
||||
v-model:value="form.dailyLimit"
|
||||
:disabled="!form.enabled"
|
||||
:min="0"
|
||||
:precision="0"
|
||||
placeholder="0 表示不限"
|
||||
style="width: 240px"
|
||||
/>
|
||||
<span class="field-desc">0 表示不限,超过当天限额后新用户会视为领取成功但不再真实发奖。</span>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="field-label">赠送奖励组</div>
|
||||
<Space wrap>
|
||||
@ -233,7 +365,7 @@ function handleReset() {
|
||||
<div class="tips">
|
||||
<div>未配置:注册成功后不发奖励。</div>
|
||||
<div>已启用:可以同时送金币和奖励组,两者至少配置一个。</div>
|
||||
<div>已关闭:注册成功后不发奖励。</div>
|
||||
<div>每日限制份数:达到上限后,后续用户会直接记为领取成功,不再真实发奖。</div>
|
||||
</div>
|
||||
|
||||
<Space class="actions" wrap>
|
||||
@ -246,6 +378,95 @@ function handleReset() {
|
||||
</Space>
|
||||
</Card>
|
||||
|
||||
<Card :bordered="false" class="record-card" title="每日领取记录">
|
||||
<div class="toolbar">
|
||||
<Space wrap>
|
||||
<DatePicker
|
||||
v-model:value="recordQuery.claimDate"
|
||||
placeholder="领取日期"
|
||||
style="width: 160px"
|
||||
value-format="YYYY-MM-DD"
|
||||
/>
|
||||
<Button :loading="recordLoading" type="primary" @click="handleRecordSearch">
|
||||
查询
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
<div class="summary">
|
||||
<Tag color="processing">
|
||||
统计日期:{{ recordSummary.date || '-' }}
|
||||
</Tag>
|
||||
<Tag color="purple">
|
||||
每日限额:{{ recordSummary.limitEnabled ? `${recordSummary.dailyLimit} 份` : '不限' }}
|
||||
</Tag>
|
||||
<Tag color="orange">
|
||||
已占用名额:{{ recordSummary.occupiedCount }}
|
||||
</Tag>
|
||||
<Tag color="success">
|
||||
已发放成功:{{ recordSummary.issuedCount }}
|
||||
</Tag>
|
||||
<Tag color="cyan">
|
||||
领取成功人数:{{ recordSummary.successCount }}
|
||||
</Tag>
|
||||
<Tag color="default">
|
||||
剩余名额:{{ recordSummary.limitEnabled ? `${recordSummary.remainingCount} 份` : '不限' }}
|
||||
</Tag>
|
||||
</div>
|
||||
|
||||
<Table
|
||||
:columns="recordColumns"
|
||||
:data-source="recordList"
|
||||
:loading="recordLoading"
|
||||
:pagination="false"
|
||||
row-key="id"
|
||||
:scroll="{ x: 1280 }"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'user'">
|
||||
<UserProfileLink
|
||||
:profile="{
|
||||
account: record.account,
|
||||
countryCode: record.countryCode,
|
||||
countryName: record.countryName,
|
||||
id: record.userId,
|
||||
userAvatar: record.userAvatar,
|
||||
userNickname: record.userNickname,
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'claimResult'">
|
||||
<Tag :color="claimResultColor(record.claimResult)">
|
||||
{{ record.claimResultText || '-' }}
|
||||
</Tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'status'">
|
||||
<Tag :color="processStatusColor(record.status)">
|
||||
{{ record.status || '-' }}
|
||||
</Tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'rewardPayload'">
|
||||
<div>金币:{{ record.goldAmount || 0 }}</div>
|
||||
<div>奖励组:{{ record.rewardGroupId || '-' }}</div>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'errorMessage'">
|
||||
{{ record.errorMessage || '-' }}
|
||||
</template>
|
||||
</template>
|
||||
</Table>
|
||||
|
||||
<div v-if="recordTotal > 0" class="pager">
|
||||
<Pagination
|
||||
:current="recordQuery.cursor"
|
||||
:page-size="recordQuery.limit"
|
||||
:total="recordTotal"
|
||||
show-size-changer
|
||||
@change="handleRecordPageChange"
|
||||
@showSizeChange="handleRecordPageChange"
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<ActivityResourceGroupSelectDrawer
|
||||
:open="groupPickerOpen"
|
||||
:sys-origin="form.sysOrigin"
|
||||
@ -258,7 +479,7 @@ function handleReset() {
|
||||
|
||||
<style scoped>
|
||||
.toolbar {
|
||||
margin-bottom: 24px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.status-desc {
|
||||
@ -278,6 +499,10 @@ function handleReset() {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.field-desc {
|
||||
color: rgb(100 116 139);
|
||||
}
|
||||
|
||||
.reward-group {
|
||||
color: rgb(15 23 42);
|
||||
}
|
||||
@ -298,4 +523,21 @@ function handleReset() {
|
||||
.actions {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.record-card {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.summary {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.pager {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 16px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -5,6 +5,7 @@ const DEV_API_INTERNAL_PREFIX = '/__dev_api_internal__';
|
||||
const DEV_API_PROD_PREFIX = '/__dev_api_prod__';
|
||||
const DEV_INTERNAL_CONSOLE_TARGET = 'http://127.0.0.1:2700';
|
||||
const DEV_INTERNAL_GOLANG_TARGET = 'http://127.0.0.1:2900';
|
||||
const DEV_PROD_GOLANG_TARGET = 'https://jvapi.haiyihy.com';
|
||||
const DEV_PROD_TARGET = 'https://yumi-admin.haiyihy.com';
|
||||
|
||||
function createPrefixRegExp(prefix: string) {
|
||||
@ -31,6 +32,13 @@ export default defineConfig(async () => {
|
||||
target: DEV_INTERNAL_CONSOLE_TARGET,
|
||||
ws: true,
|
||||
},
|
||||
[`${DEV_API_PROD_PREFIX}/go`]: {
|
||||
changeOrigin: true,
|
||||
rewrite: (path) =>
|
||||
path.replace(createPrefixRegExp(DEV_API_PROD_PREFIX), ''),
|
||||
target: DEV_PROD_GOLANG_TARGET,
|
||||
ws: true,
|
||||
},
|
||||
[DEV_API_PROD_PREFIX]: {
|
||||
changeOrigin: true,
|
||||
rewrite: (path) =>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user