diff --git a/src/api/aslan-game-king.js b/src/api/aslan-game-king.js
index 8eaa092..20b8411 100644
--- a/src/api/aslan-game-king.js
+++ b/src/api/aslan-game-king.js
@@ -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
})
}
diff --git a/src/views/activity/game-king/index.vue b/src/views/activity/game-king/index.vue
index f4ef1b4..3e600f9 100644
--- a/src/views/activity/game-king/index.vue
+++ b/src/views/activity/game-king/index.vue
@@ -90,7 +90,7 @@
type="warning"
:closable="false"
show-icon
- title="后端判定活动已经开始:统计、结算、启用状态、七个奖项和六档排行奖励均已锁定;仅名称与说明可维护。"
+ title="后端判定活动已经开始:统计、结算、启用状态、七个奖项和日榜/总榜排行奖励均已锁定;仅名称与说明可维护。"
/>
基础配置
@@ -193,7 +193,7 @@
-
+
@@ -260,14 +260,21 @@
当前总权重:{{ totalPrizeWeight }}
- 排行榜结算奖励(每个榜单固定六档)
+ 排行榜结算奖励(日榜 Top3 + 总榜六档)
+
+
+
+ {{ scope.row.periodType === 'DAILY' ? '日榜' : '总榜' }}
+
+
+
@@ -310,17 +317,30 @@
:close-on-click-modal="false"
>
+
+
+
+
+
刷新
手工结算
- 到达“结束时间 + 结算等待”后才可执行;UNKNOWN 必须按奖励项 ID 核账,禁止直接重发。
+ 日榜按活动时区在自然日结束并等待配置分钟后冻结;UNKNOWN 必须核账,禁止直接重发。
@@ -368,6 +388,12 @@
+
+
+ {{ scope.row.periodType === 'DAILY' ? '日榜' : '总榜' }}
+ {{ scope.row.statDate || '-' }}
+
+
@@ -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 核账
// 走各自专用链路,避免运营把“继续发放”误当成再次生成快照。