feat: configure game king daily rewards

This commit is contained in:
zhx 2026-07-22 20:48:22 +08:00
parent cc455ccfb8
commit 942e678f1a
2 changed files with 117 additions and 35 deletions

View File

@ -63,10 +63,11 @@ export function saveAslanGameKingRankRewards(id, rankRewards) {
}) })
} }
export function listAslanGameKingSettlementRecords(id) { export function listAslanGameKingSettlementRecords(id, params) {
return request({ return request({
url: `${baseUrl}/settlement-records/${id}`, url: `${baseUrl}/settlement-records/${id}`,
method: 'get' method: 'get',
params
}) })
} }
@ -93,10 +94,11 @@ export function resolveAslanGameKingUnknownDeliveryItem(itemId, delivered) {
}) })
} }
export function settleAslanGameKingActivity(id) { export function settleAslanGameKingActivity(id, params) {
return request({ return request({
url: `${baseUrl}/settlement/${id}`, url: `${baseUrl}/settlement/${id}`,
method: 'post' method: 'post',
params
}) })
} }

View File

@ -90,7 +90,7 @@
type="warning" type="warning"
:closable="false" :closable="false"
show-icon show-icon
title="后端判定活动已经开始:统计、结算、启用状态、七个奖项和六档排行奖励均已锁定;仅名称与说明可维护。" title="后端判定活动已经开始:统计、结算、启用状态、七个奖项和日榜/总榜排行奖励均已锁定;仅名称与说明可维护。"
/> />
<div class="section-title">基础配置</div> <div class="section-title">基础配置</div>
@ -193,7 +193,7 @@
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="排行周期"> <el-form-item label="排行周期">
<el-input value="活动总榜OVERALL" disabled /> <el-input value="日榜DAILY+ 活动总榜OVERALL" disabled />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24"> <el-col :span="24">
@ -260,14 +260,21 @@
</el-table> </el-table>
<div class="weight-summary">当前总权重{{ totalPrizeWeight }}</div> <div class="weight-summary">当前总权重{{ totalPrizeWeight }}</div>
<div class="section-title">排行榜结算奖励每个榜单固定六档</div> <div class="section-title">排行榜结算奖励日榜 Top3 + 总榜六档</div>
<el-alert <el-alert
type="warning" type="warning"
:closable="false" :closable="false"
show-icon show-icon
title="消费榜和中奖榜分别配置六档奖励;名次范围不可增删或修改,失败记录可在结算记录中单独重试。" title="消费榜和中奖榜均配置 DAILY Top1/Top2/Top3OVERALL 保留六档;名次范围不可修改。"
/> />
<el-table :data="form.rankRewards" border class="config-table"> <el-table :data="form.rankRewards" border class="config-table">
<el-table-column label="周期" width="100" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.periodType === 'DAILY' ? 'success' : 'info'">
{{ scope.row.periodType === 'DAILY' ? '日榜' : '总榜' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="榜单" width="110" align="center"> <el-table-column label="榜单" width="110" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<el-tag :type="scope.row.rankingType === 'VICTORIOUS' ? 'warning' : 'primary'"> <el-tag :type="scope.row.rankingType === 'VICTORIOUS' ? 'warning' : 'primary'">
@ -310,17 +317,30 @@
:close-on-click-modal="false" :close-on-click-modal="false"
> >
<div class="dialog-actions"> <div class="dialog-actions">
<el-select v-model="settlementPeriodType" style="width: 130px" @change="changeSettlementPeriod">
<el-option label="活动总榜" value="OVERALL" />
<el-option label="每日榜" value="DAILY" />
</el-select>
<el-date-picker
v-if="settlementPeriodType === 'DAILY'"
v-model="settlementStatDate"
type="date"
value-format="yyyy-MM-dd"
placeholder="选择活动日期"
style="width: 165px"
@change="loadSettlements"
/>
<el-button icon="el-icon-refresh" :loading="settlementsLoading" @click="loadSettlements">刷新</el-button> <el-button icon="el-icon-refresh" :loading="settlementsLoading" @click="loadSettlements">刷新</el-button>
<el-button <el-button
type="danger" type="danger"
icon="el-icon-s-check" icon="el-icon-s-check"
:disabled="!settlementReady(selectedActivity)" :disabled="!manualSettlementReady()"
:loading="settleLoading" :loading="settleLoading"
@click="manualSettle" @click="manualSettle"
> >
手工结算 手工结算
</el-button> </el-button>
<span class="form-help">到达结束时间 + 结算等待后才可执行UNKNOWN 必须按奖励项 ID 核账禁止直接重发</span> <span class="form-help">日榜按活动时区在自然日结束并等待配置分钟后冻结UNKNOWN 必须核账禁止直接重发</span>
</div> </div>
<el-table v-loading="settlementsLoading" :data="settlementRecords" border max-height="570"> <el-table v-loading="settlementsLoading" :data="settlementRecords" border max-height="570">
<el-table-column type="expand" width="50"> <el-table-column type="expand" width="50">
@ -368,6 +388,12 @@
</el-table> </el-table>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="周期 / 日期" width="135" align="center">
<template slot-scope="scope">
<div>{{ scope.row.periodType === 'DAILY' ? '日榜' : '总榜' }}</div>
<div class="secondary-text">{{ scope.row.statDate || '-' }}</div>
</template>
</el-table-column>
<el-table-column label="榜单" width="110" align="center"> <el-table-column label="榜单" width="110" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<el-tag :type="scope.row.rankingType === 'VICTORIOUS' ? 'warning' : 'primary'"> <el-tag :type="scope.row.rankingType === 'VICTORIOUS' ? 'warning' : 'primary'">
@ -564,7 +590,7 @@ import {
settleAslanGameKingActivity settleAslanGameKingActivity
} from '@/api/aslan-game-king' } from '@/api/aslan-game-king'
const REQUIRED_RANK_RANGES = [ const OVERALL_RANK_RANGES = [
[1, 1], [1, 1],
[2, 2], [2, 2],
[3, 3], [3, 3],
@ -572,7 +598,12 @@ const REQUIRED_RANK_RANGES = [
[8, 10], [8, 10],
[11, 30] [11, 30]
] ]
const DAILY_RANK_RANGES = [[1, 1], [2, 2], [3, 3]]
const RANKING_TYPES = ['TYCOON', 'VICTORIOUS'] const RANKING_TYPES = ['TYCOON', 'VICTORIOUS']
const RANK_REWARD_SCOPES = [
{ periodType: 'DAILY', ranges: DAILY_RANK_RANGES },
{ periodType: 'OVERALL', ranges: OVERALL_RANK_RANGES }
]
export default { export default {
name: 'ActivityGameKing', name: 'ActivityGameKing',
@ -605,6 +636,8 @@ export default {
settlementsLoading: false, settlementsLoading: false,
settleLoading: false, settleLoading: false,
settlementRecords: [], settlementRecords: [],
settlementPeriodType: 'OVERALL',
settlementStatDate: '',
retryingRecordId: '', retryingRecordId: '',
drawRecordsVisible: false, drawRecordsVisible: false,
drawRecordsLoading: false, drawRecordsLoading: false,
@ -673,10 +706,12 @@ export default {
})) }))
}, },
emptyRankRewards() { emptyRankRewards() {
return RANKING_TYPES.reduce((result, rankingType) => result.concat( return RANK_REWARD_SCOPES.reduce((periodResult, scope) => periodResult.concat(
REQUIRED_RANK_RANGES.map((range, index) => ({ RANKING_TYPES.reduce((boardResult, rankingType) => boardResult.concat(
scope.ranges.map((range, index) => ({
id: '', id: '',
activityId: '', activityId: '',
periodType: scope.periodType,
rankingType, rankingType,
startRank: range[0], startRank: range[0],
endRank: range[1], endRank: range[1],
@ -685,6 +720,7 @@ export default {
rewards: [] rewards: []
})) }))
), []) ), [])
), [])
}, },
loadActivities() { loadActivities() {
this.listLoading = true this.listLoading = true
@ -744,9 +780,11 @@ export default {
mergeRankRewards(rankRewards) { mergeRankRewards(rankRewards) {
const result = this.emptyRankRewards() const result = this.emptyRankRewards()
rankRewards.forEach(item => { rankRewards.forEach(item => {
// // DAILY OVERALL
const periodType = item.periodType || 'OVERALL'
const rankingType = item.rankingType || 'TYCOON' const rankingType = item.rankingType || 'TYCOON'
const index = result.findIndex(target => const index = result.findIndex(target =>
target.periodType === periodType &&
target.rankingType === rankingType && target.rankingType === rankingType &&
target.startRank === Number(item.startRank) && target.startRank === Number(item.startRank) &&
target.endRank === Number(item.endRank) target.endRank === Number(item.endRank)
@ -777,7 +815,7 @@ export default {
} }
const payload = this.buildSavePayload() const payload = this.buildSavePayload()
this.saveLoading = true this.saveLoading = true
// // DAILY/OVERALL
saveAslanGameKingActivity(payload).then(() => { saveAslanGameKingActivity(payload).then(() => {
this.$message.success('活动配置已保存') this.$message.success('活动配置已保存')
this.editorVisible = false this.editorVisible = false
@ -823,15 +861,14 @@ export default {
this.$message.error('请补全 7 个奖项的名称、图片、正整数权重、合法库存和奖励资源组') this.$message.error('请补全 7 个奖项的名称、图片、正整数权重、合法库存和奖励资源组')
return false return false
} }
const rangesValid = this.form.rankRewards.length === REQUIRED_RANK_RANGES.length * RANKING_TYPES.length && const expectedKeys = this.emptyRankRewards().map(item =>
this.form.rankRewards.every((item, index) => `${item.periodType}:${item.rankingType}:${item.startRank}:${item.endRank}`)
item.rankingType === RANKING_TYPES[Math.floor(index / REQUIRED_RANK_RANGES.length)] && const actualKeys = this.form.rankRewards.map(item =>
Number(item.startRank) === REQUIRED_RANK_RANGES[index % REQUIRED_RANK_RANGES.length][0] && `${item.periodType}:${item.rankingType}:${Number(item.startRank)}:${Number(item.endRank)}`)
Number(item.endRank) === REQUIRED_RANK_RANGES[index % REQUIRED_RANK_RANGES.length][1] && const rangesValid = actualKeys.length === expectedKeys.length &&
Boolean(item.resourceGroupId) actualKeys.every((key, index) => key === expectedKeys[index] && Boolean(this.form.rankRewards[index].resourceGroupId))
)
if (!rangesValid) { if (!rangesValid) {
this.$message.error('两个榜单的六档名次范围必须保持固定,并为每档选择奖励资源组') this.$message.error('请完整配置两个榜单的 DAILY Top3 与 OVERALL 六档奖励资源组')
return false return false
} }
return true return true
@ -869,6 +906,7 @@ export default {
payload.rankRewards = this.form.rankRewards.map(item => ({ payload.rankRewards = this.form.rankRewards.map(item => ({
id: item.id || null, id: item.id || null,
activityId: this.form.id || null, activityId: this.form.id || null,
periodType: item.periodType,
rankingType: item.rankingType, rankingType: item.rankingType,
startRank: Number(item.startRank), startRank: Number(item.startRank),
endRank: Number(item.endRank), endRank: Number(item.endRank),
@ -889,6 +927,11 @@ export default {
this.resourceSelection = { type, index } this.resourceSelection = { type, index }
this.resourceSelectorVisible = true this.resourceSelectorVisible = true
}, },
firstResourceGroupCover(source) {
const firstReward = Array.isArray(source.rewardConfigList) ? source.rewardConfigList[0] : null
const cover = firstReward && (firstReward.cover || firstReward.coverUrl)
return cover ? this.$getAccessImgUrl(String(cover).trim()) : ''
},
applyResourceGroup(source) { applyResourceGroup(source) {
if (!this.resourceSelection) { if (!this.resourceSelection) {
return return
@ -898,6 +941,13 @@ export default {
// Long ID Java Snowflake ID // Long ID Java Snowflake ID
row.resourceGroupId = String(source.id) row.resourceGroupId = String(source.id)
row.rewards = source.rewardConfigList || [] row.rewards = source.rewardConfigList || []
if (this.resourceSelection.type === 'prize') {
// H5
row.prizeImage = this.firstResourceGroupCover(source)
if (!row.prizeImage) {
this.$message.warning('资源组第一个奖励没有封面图,请更换资源组或手动上传奖项图片')
}
}
if (this.resourceSelection.type === 'rank' && !row.rewardName) { if (this.resourceSelection.type === 'rank' && !row.rewardName) {
row.rewardName = source.name || '' row.rewardName = source.name || ''
} }
@ -919,26 +969,49 @@ export default {
}, },
openSettlements(row) { openSettlements(row) {
this.selectedActivity = row this.selectedActivity = row
this.settlementPeriodType = 'OVERALL'
this.settlementStatDate = ''
this.settlementsVisible = true this.settlementsVisible = true
this.loadSettlements() this.loadSettlements()
}, },
changeSettlementPeriod() {
this.settlementRecords = []
if (this.settlementPeriodType === 'OVERALL') {
this.settlementStatDate = ''
this.loadSettlements()
}
},
loadSettlements() { loadSettlements() {
if (!this.selectedActivity.id) { if (!this.selectedActivity.id) {
return return
} }
if (this.settlementPeriodType === 'DAILY' && !this.settlementStatDate) {
this.settlementRecords = []
return
}
const params = { periodType: this.settlementPeriodType }
if (this.settlementPeriodType === 'DAILY') {
params.statDate = this.settlementStatDate
}
this.settlementsLoading = true this.settlementsLoading = true
listAslanGameKingSettlementRecords(this.selectedActivity.id).then(res => { listAslanGameKingSettlementRecords(this.selectedActivity.id, params).then(res => {
this.settlementRecords = Array.isArray(res.body) ? res.body : [] this.settlementRecords = Array.isArray(res.body) ? res.body : []
}).finally(() => { }).finally(() => {
this.settlementsLoading = false this.settlementsLoading = false
}) })
}, },
manualSettle() { manualSettle() {
this.$confirm('将按活动结束时最终消费榜生成 Top 30 奖励并立即尝试发放,确认继续?', '手工结算', { const daily = this.settlementPeriodType === 'DAILY'
const params = { periodType: this.settlementPeriodType }
if (daily) {
params.statDate = this.settlementStatDate
}
const target = daily ? `${this.settlementStatDate} 日榜双榜 Top3` : '活动总榜双榜 Top30'
this.$confirm(`将冻结${target}并立即尝试发放,确认继续?`, '手工结算', {
type: 'warning' type: 'warning'
}).then(() => { }).then(() => {
this.settleLoading = true this.settleLoading = true
return settleAslanGameKingActivity(this.selectedActivity.id) return settleAslanGameKingActivity(this.selectedActivity.id, params)
}).then(() => { }).then(() => {
this.$message.success('结算任务已执行') this.$message.success('结算任务已执行')
this.loadSettlements() this.loadSettlements()
@ -1054,6 +1127,13 @@ export default {
hasStarted(row) { hasStarted(row) {
return Boolean(row && row.startTime && Date.now() >= Number(row.startTime)) return Boolean(row && row.startTime && Date.now() >= Number(row.startTime))
}, },
manualSettlementReady() {
if (this.settlementPeriodType === 'DAILY') {
// /DST
return Boolean(this.selectedActivity.id && this.settlementStatDate)
}
return this.settlementReady(this.selectedActivity)
},
settlementReady(row) { settlementReady(row) {
// PROCESSING/PARTIAL_FAILED UNKNOWN // PROCESSING/PARTIAL_FAILED UNKNOWN
// //