From 2419f81955eb93a79ce9f33c7623151cec7ae6be Mon Sep 17 00:00:00 2001
From: zhenhui <1276357500@qq.com>
Date: Fri, 13 Mar 2026 16:51:56 +0800
Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=AF=BC=E5=85=A5=E6=9D=83?=
=?UTF-8?q?=E9=87=8D=E6=B5=8B=E8=AF=95=E6=95=B0=E6=8D=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../index/modules/detail-drawer.vue | 162 +++++++++++++++---
.../app/dice/logic/reward/DiceRewardLogic.php | 16 +-
.../DiceRewardConfigRecordLogic.php | 89 +++++++---
.../reward_config_record/WeightTestRunner.php | 8 +-
.../DiceRewardConfigRecord.php | 30 +++-
5 files changed, 251 insertions(+), 54 deletions(-)
diff --git a/saiadmin-artd/src/views/plugin/dice/reward_config_record/index/modules/detail-drawer.vue b/saiadmin-artd/src/views/plugin/dice/reward_config_record/index/modules/detail-drawer.vue
index 56c04cd..ce8920e 100644
--- a/saiadmin-artd/src/views/plugin/dice/reward_config_record/index/modules/detail-drawer.vue
+++ b/saiadmin-artd/src/views/plugin/dice/reward_config_record/index/modules/detail-drawer.vue
@@ -27,6 +27,14 @@
{{ record.free_lottery_config_id ?? '—' }}
+
+
+
+ {{ item.grid }}:{{ item.weight }}
+
+
+ —
+
@@ -68,19 +76,53 @@
权重配比快照(测试时使用的 T1-T5/BIGWIN 配置)
-
-
-
-
-
-
-
暂无快照数据
+
+
+
顺时针(非 BIGWIN)
+
+
+
+
+
+
暂无顺时针数据
+
+
+
+
逆时针(非 BIGWIN)
+
+
+
+
+
+
暂无逆时针数据
+
+
+
+
BIGWIN(按 DiceRewardConfig 配置快照)
+
+
+
+
+
暂无 BIGWIN 数据
+
@@ -181,6 +223,7 @@
lottery_config_id?: number | null
paid_lottery_config_id?: number | null
free_lottery_config_id?: number | null
+ bigwin_weight?: Record | Array<[number, number]> | null
// 新结构:{ paid: {T1..T5}, free: {T1..T5} },兼容旧结构直接是 {T1..T5}
tier_weights_snapshot?:
| {
@@ -279,6 +322,26 @@
return tierWeightsToTableData(source || undefined)
})
+ const bigwinWeightDisplay = computed(() => {
+ const raw = props.record?.bigwin_weight
+ if (!raw) return []
+ const entries: Array<{ grid: number; weight: number }> = []
+ if (Array.isArray(raw)) {
+ raw.forEach(([grid, weight]) => {
+ entries.push({ grid: Number(grid), weight: Number(weight) })
+ })
+ } else if (typeof raw === 'object') {
+ Object.keys(raw).forEach((k) => {
+ const grid = Number(k)
+ const w = Number((raw as Record)[k])
+ if (!Number.isNaN(grid) && !Number.isNaN(w)) {
+ entries.push({ grid, weight: w })
+ }
+ })
+ }
+ return entries.sort((a, b) => a.grid - b.grid)
+ })
+
// 导入不限制奖池类型,两个下拉都可选任意 DiceLotteryPoolConfig
const paidLotteryOptions = computed(() => lotteryConfigOptions.value)
const freeLotteryOptions = computed(() => lotteryConfigOptions.value)
@@ -289,25 +352,65 @@
})
const snapshotTableData = computed(() => {
- const snapshot = props.record?.weight_config_snapshot
+ const snapshot = props.record?.weight_config_snapshot as
+ | Array<{
+ tier?: string
+ direction?: number
+ grid_number?: number
+ weight?: number
+ }>
+ | undefined
if (!Array.isArray(snapshot)) return []
- return snapshot.map((item) => ({
- id: item.id ?? '—',
- grid_number: item.grid_number ?? '—',
- tier: item.tier ?? '—',
- weight: item.weight ?? '—'
- }))
+ return snapshot.map((item) => {
+ const dir = item.direction
+ return {
+ tier: item.tier ?? '—',
+ direction: dir,
+ direction_label: dir === 0 ? '顺时针' : dir === 1 ? '逆时针' : '—',
+ grid_number: item.grid_number ?? '—',
+ weight: item.weight ?? '—'
+ }
+ })
})
+ const snapshotClockwise = computed(() =>
+ snapshotTableData.value.filter((row) => row.direction === 0 && row.tier !== 'BIGWIN')
+ )
+ const snapshotCounterclockwise = computed(() =>
+ snapshotTableData.value.filter((row) => row.direction === 1 && row.tier !== 'BIGWIN')
+ )
+ const bigwinTableData = computed(() =>
+ bigwinWeightDisplay.value.map((item) => ({
+ grid_number: item.grid,
+ weight: item.weight
+ }))
+ )
+
const chartLabels = computed(() => GRID_NUMBERS.map((n) => String(n)))
const chartData = computed(() => {
const counts = props.record?.result_counts
- if (!counts || typeof counts !== 'object') return GRID_NUMBERS.map(() => 0)
- return GRID_NUMBERS.map((n) => {
- const v = counts[String(n)] ?? counts[n]
- return typeof v === 'number' && !Number.isNaN(v) ? v : 0
- })
+ if (!counts) return GRID_NUMBERS.map(() => 0)
+
+ // 兼容两种结构:对象 {5:10,...} 或数组 [10, ...]
+ if (Array.isArray(counts)) {
+ // 如果是数组,按顺序映射到 GRID_NUMBERS
+ return GRID_NUMBERS.map((_, idx) => {
+ const v = counts[idx]
+ return typeof v === 'number' && !Number.isNaN(v) ? v : 0
+ })
+ }
+
+ if (typeof counts === 'object') {
+ return GRID_NUMBERS.map((n) => {
+ const byString = (counts as Record)[String(n)]
+ const byNumber = (counts as Record)[n]
+ const v = byString ?? byNumber
+ return typeof v === 'number' && !Number.isNaN(v) ? v : 0
+ })
+ }
+
+ return GRID_NUMBERS.map(() => 0)
})
const resultTotal = computed(() => {
@@ -385,6 +488,15 @@
.snapshot-table {
margin-bottom: 8px;
}
+ .snapshot-group {
+ margin-bottom: 12px;
+ }
+ .snapshot-subtitle {
+ font-size: 13px;
+ font-weight: 500;
+ margin: 4px 0;
+ color: var(--el-text-color-secondary);
+ }
.chart-wrap {
margin-bottom: 8px;
}
diff --git a/server/app/dice/logic/reward/DiceRewardLogic.php b/server/app/dice/logic/reward/DiceRewardLogic.php
index 43bcbaf..f23a3eb 100644
--- a/server/app/dice/logic/reward/DiceRewardLogic.php
+++ b/server/app/dice/logic/reward/DiceRewardLogic.php
@@ -354,9 +354,13 @@ class DiceRewardLogic
if ($configCw !== null) {
$tier = isset($configCw['tier']) ? trim((string) $configCw['tier']) : '';
if ($tier !== '') {
+ // 使用对应奖励配置的 weight 作为格子权重(若未配置则退回最小权重)
+ $weightCw = isset($configCw['weight']) && $configCw['weight'] !== null
+ ? $configCw['weight']
+ : self::WEIGHT_MIN;
$payloadCw = [
'tier' => $tier,
- 'weight' => self::WEIGHT_MIN,
+ 'weight' => $weightCw,
'grid_number' => $gridNumber,
'start_index' => $startId,
'end_index' => $endIdCw,
@@ -374,7 +378,7 @@ class DiceRewardLogic
$m->tier = $tier;
$m->direction = DiceReward::DIRECTION_CLOCKWISE;
$m->end_index = $endIdCw;
- $m->weight = self::WEIGHT_MIN;
+ $m->weight = $weightCw;
$m->grid_number = $gridNumber;
$m->start_index = $startId;
$m->ui_text = $configCw['ui_text'] ?? '';
@@ -390,9 +394,13 @@ class DiceRewardLogic
if ($configCcw !== null) {
$tier = isset($configCcw['tier']) ? trim((string) $configCcw['tier']) : '';
if ($tier !== '') {
+ // 使用对应奖励配置的 weight 作为格子权重(若未配置则退回最小权重)
+ $weightCcw = isset($configCcw['weight']) && $configCcw['weight'] !== null
+ ? $configCcw['weight']
+ : self::WEIGHT_MIN;
$payloadCcw = [
'tier' => $tier,
- 'weight' => self::WEIGHT_MIN,
+ 'weight' => $weightCcw,
'grid_number' => $gridNumber,
'start_index' => $startId,
'end_index' => $endIdCcw,
@@ -410,7 +418,7 @@ class DiceRewardLogic
$m->tier = $tier;
$m->direction = DiceReward::DIRECTION_COUNTERCLOCKWISE;
$m->end_index = $endIdCcw;
- $m->weight = self::WEIGHT_MIN;
+ $m->weight = $weightCcw;
$m->grid_number = $gridNumber;
$m->start_index = $startId;
$m->ui_text = $configCcw['ui_text'] ?? '';
diff --git a/server/app/dice/logic/reward_config_record/DiceRewardConfigRecordLogic.php b/server/app/dice/logic/reward_config_record/DiceRewardConfigRecordLogic.php
index c4d377a..8d3e6e2 100644
--- a/server/app/dice/logic/reward_config_record/DiceRewardConfigRecordLogic.php
+++ b/server/app/dice/logic/reward_config_record/DiceRewardConfigRecordLogic.php
@@ -91,40 +91,69 @@ class DiceRewardConfigRecordLogic extends BaseLogic
}
if (is_array($snapshot) && !empty($snapshot)) {
foreach ($snapshot as $item) {
- $id = isset($item['id']) ? (int) $item['id'] : 0;
+ $direction = isset($item['direction']) ? (int) $item['direction'] : null;
+ $gridNumber = isset($item['grid_number']) ? (int) $item['grid_number'] : 0;
$weight = isset($item['weight']) ? (int) $item['weight'] : 1;
$weight = max(1, min(10000, $weight));
- if ($id <= 0) {
+ if (!in_array($direction, [DiceReward::DIRECTION_CLOCKWISE, DiceReward::DIRECTION_COUNTERCLOCKWISE], true)) {
+ continue;
+ }
+ if ($gridNumber <= 0) {
continue;
}
$tier = isset($item['tier']) ? (string) $item['tier'] : '';
if ($tier === '') {
- $tier = DiceRewardConfig::where('id', $id)->value('tier');
- $tier = $tier !== null && $tier !== '' ? (string) $tier : '';
+ // 若快照中未带 tier,则尝试按方向+点数从现有配置中取
+ $tierFromDb = DiceReward::where('direction', $direction)->where('grid_number', $gridNumber)->value('tier');
+ $tier = $tierFromDb !== null ? (string) $tierFromDb : '';
}
- if ($tier === '') {
- continue;
- }
- // 写入 DiceReward(顺/逆时针同值)
- foreach ([DiceReward::DIRECTION_CLOCKWISE, DiceReward::DIRECTION_COUNTERCLOCKWISE] as $direction) {
- $affected = DiceReward::where('tier', $tier)->where('direction', $direction)->where('end_index', $id)->update(['weight' => $weight]);
- if ($affected === 0) {
- $m = new DiceReward();
- $m->tier = $tier;
- $m->direction = $direction;
- $m->end_index = $id;
- $m->weight = $weight;
- $m->save();
+ // 仅按方向 + 点数更新 DiceReward(若存在则更新,不存在才插入,避免唯一键冲突)
+ $reward = DiceReward::where('direction', $direction)
+ ->where('grid_number', $gridNumber)
+ ->find();
+ if ($reward) {
+ $reward->weight = $weight;
+ // 若快照中有 tier,补齐 tier 信息
+ if ($tier !== '' && (string) $reward->tier !== $tier) {
+ $reward->tier = $tier;
}
- }
- // BIGWIN:同步写入 DiceRewardConfig.weight
- if (strtoupper($tier) === 'BIGWIN') {
- DiceRewardConfig::where('id', $id)->update(['weight' => $weight]);
+ $reward->save();
+ } else {
+ $m = new DiceReward();
+ if ($tier !== '') {
+ $m->tier = $tier;
+ }
+ $m->direction = $direction;
+ $m->grid_number = $gridNumber;
+ $m->weight = $weight;
+ $m->save();
}
}
DiceReward::refreshCache();
}
+ // 使用记录中的 bigwin_weight JSON 将 BIGWIN 概率导入到 DiceRewardConfig
+ $recordBigwinWeight = $record['bigwin_weight'] ?? null;
+ if (is_string($recordBigwinWeight)) {
+ $decoded = json_decode($recordBigwinWeight, true);
+ $recordBigwinWeight = is_array($decoded) ? $decoded : null;
+ }
+ if (is_array($recordBigwinWeight) && !empty($recordBigwinWeight)) {
+ foreach ($recordBigwinWeight as $grid => $w) {
+ $gridNumber = (int) $grid;
+ $weight = (int) $w;
+ if ($gridNumber <= 0) {
+ continue;
+ }
+ if ($weight < 0) {
+ $weight = 0;
+ }
+ DiceRewardConfig::where('tier', 'BIGWIN')
+ ->where('grid_number', $gridNumber)
+ ->update(['weight' => $weight]);
+ }
+ }
+
$tiers = ['T1', 'T2', 'T3', 'T4', 'T5'];
$tierSnapshot = $record['tier_weights_snapshot'] ?? null;
if (is_string($tierSnapshot)) {
@@ -251,13 +280,15 @@ class DiceRewardConfigRecordLogic extends BaseLogic
$paidTierWeights = null;
$freeTierWeights = null;
+ // 来自 DiceReward 的当前权重快照(按方向+点数),用于权重测试模拟
$instance = DiceReward::getCachedInstance();
$byTierDirection = $instance['by_tier_direction'] ?? [];
foreach ($byTierDirection as $tier => $byDir) {
foreach ($byDir as $dir => $rows) {
foreach ($rows as $row) {
$snapshot[] = [
- 'id' => (int) ($row['id'] ?? 0),
+ // 不再记录 DiceReward.id,只记录方向、点数和、档位与权重
+ 'direction' => (int) $dir,
'grid_number' => (int) ($row['grid_number'] ?? 0),
'tier' => (string) ($tier ?? ''),
'weight' => (int) ($row['weight'] ?? 0),
@@ -266,6 +297,19 @@ class DiceRewardConfigRecordLogic extends BaseLogic
}
}
+ // BIGWIN 概率快照从 DiceRewardConfig 读取(例如豹子号配置)
+ // JSON 结构 {"grid_number": weight, ...}
+ $bigwinWeights = [];
+ $bigwinConfigs = DiceRewardConfig::getCachedByTier('BIGWIN');
+ foreach ($bigwinConfigs as $cfg) {
+ $grid = isset($cfg['grid_number']) ? (int) $cfg['grid_number'] : 0;
+ if ($grid <= 0) {
+ continue;
+ }
+ $w = isset($cfg['weight']) ? (int) $cfg['weight'] : 0;
+ $bigwinWeights[$grid] = $w;
+ }
+
if ($paidConfigId > 0) {
$config = DiceLotteryPoolConfig::find($paidConfigId);
if (!$config) {
@@ -359,6 +403,7 @@ class DiceRewardConfigRecordLogic extends BaseLogic
$record->free_tier_weights = $freeTierWeights;
$record->result_counts = [];
$record->tier_counts = null;
+ $record->bigwin_weight = $bigwinWeights ?: null;
$record->admin_id = $adminId;
$record->create_time = date('Y-m-d H:i:s');
$record->save();
diff --git a/server/app/dice/logic/reward_config_record/WeightTestRunner.php b/server/app/dice/logic/reward_config_record/WeightTestRunner.php
index fc23ab7..376b633 100644
--- a/server/app/dice/logic/reward_config_record/WeightTestRunner.php
+++ b/server/app/dice/logic/reward_config_record/WeightTestRunner.php
@@ -196,11 +196,15 @@ class WeightTestRunner
*/
private function markSuccess(int $recordId, array $resultCounts, array $tierCounts): void
{
- $platformProfit = DiceRewardConfigRecord::computePlatformProfitFromRelated($recordId);
$record = DiceRewardConfigRecord::find($recordId);
if ($record) {
+ // 平台盈利通过关联测试记录统计
+ $platformProfit = DiceRewardConfigRecord::computePlatformProfitFromRelated($recordId);
+ // 落点统计也通过关联测试记录重新统计,避免模拟过程异常导致为空
+ $dbResultCounts = DiceRewardConfigRecord::computeResultCountsFromRelated($recordId);
+
$record->status = DiceRewardConfigRecord::STATUS_SUCCESS;
- $record->result_counts = $resultCounts;
+ $record->result_counts = !empty($dbResultCounts) ? $dbResultCounts : $resultCounts;
$record->tier_counts = $tierCounts;
$record->remark = null;
$record->platform_profit = $platformProfit;
diff --git a/server/app/dice/model/reward_config_record/DiceRewardConfigRecord.php b/server/app/dice/model/reward_config_record/DiceRewardConfigRecord.php
index 1480b4d..6fa0da1 100644
--- a/server/app/dice/model/reward_config_record/DiceRewardConfigRecord.php
+++ b/server/app/dice/model/reward_config_record/DiceRewardConfigRecord.php
@@ -37,6 +37,7 @@ use think\model\relation\HasMany;
* @property array $result_counts 落点统计 grid_number=>出现次数
* @property array|null $tier_counts 档位出现次数 T1=>count
* @property float|null $platform_profit 平台赚取金额(付费抽取次数×100-玩家总收益)
+ * @property array|null $bigwin_weight 测试时 BIGWIN 档位权重快照(JSON:grid_number=>weight)
* @property int|null $admin_id 执行测试的管理员ID
* @property string|null $create_time 创建时间
*/
@@ -55,7 +56,7 @@ class DiceRewardConfigRecord extends BaseModel
protected $table = 'dice_reward_config_record';
- protected $json = ['weight_config_snapshot', 'tier_weights_snapshot', 'result_counts', 'tier_counts', 'paid_tier_weights', 'free_tier_weights'];
+ protected $json = ['weight_config_snapshot', 'tier_weights_snapshot', 'result_counts', 'tier_counts', 'paid_tier_weights', 'free_tier_weights', 'bigwin_weight'];
protected $jsonAssoc = true;
@@ -82,4 +83,31 @@ class DiceRewardConfigRecord extends BaseModel
->sum('win_coin');
return round($paidCount * 100 - $sumWinCoin, 2);
}
+
+ /**
+ * 根据关联的 DicePlayRecordTest 统计落点次数
+ * result_counts = [grid_number => 出现次数],只统计 roll_number 在 5-30 之间的记录
+ * @param int $recordId
+ * @return array
+ */
+ public static function computeResultCountsFromRelated(int $recordId): array
+ {
+ $rows = DicePlayRecordTest::where('reward_config_record_id', $recordId)
+ ->where('roll_number', '>=', 5)
+ ->where('roll_number', '<=', 30)
+ ->field('roll_number, COUNT(*) AS c')
+ ->group('roll_number')
+ ->select()
+ ->toArray();
+
+ $result = [];
+ foreach ($rows as $row) {
+ $grid = (int) ($row['roll_number'] ?? 0);
+ $cnt = (int) ($row['c'] ?? 0);
+ if ($grid > 0 && $cnt > 0) {
+ $result[$grid] = $cnt;
+ }
+ }
+ return $result;
+ }
}