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({
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({
url: `${baseUrl}/settlement/${id}`,
method: 'post'
method: 'post',
params
})
}

View File

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