diff --git a/saiadmin-artd/src/locales/langs/en/dice/play_record_test.json b/saiadmin-artd/src/locales/langs/en/dice/play_record_test.json index d4b99e3..3d0167f 100644 --- a/saiadmin-artd/src/locales/langs/en/dice/play_record_test.json +++ b/saiadmin-artd/src/locales/langs/en/dice/play_record_test.json @@ -8,6 +8,8 @@ "direction": "Direction", "isBigWin": "Is Big Win", "winCoin": "Win Coin", + "paidAmount": "Paid Amount", + "ante": "Ante", "rewardTier": "Reward Tier", "rollNumber": "Roll Number", "paid": "Paid", @@ -23,6 +25,8 @@ "lotteryPoolConfig": "Lottery Pool Config", "drawType": "Draw Type", "isBigWin": "Is Big Win", + "paidAmount": "Paid Amount", + "ante": "Ante", "winCoin": "Win Coin", "superWinCoin": "Super Win Coin", "rewardWinCoin": "Reward Win Coin", diff --git a/saiadmin-artd/src/locales/langs/en/dice/player_ticket_record.json b/saiadmin-artd/src/locales/langs/en/dice/player_ticket_record.json index f3d1799..cd2c422 100644 --- a/saiadmin-artd/src/locales/langs/en/dice/player_ticket_record.json +++ b/saiadmin-artd/src/locales/langs/en/dice/player_ticket_record.json @@ -19,6 +19,7 @@ "search": { "player": "Player", "useCoins": "Use Coins", + "ante": "Ante", "totalDrawCount": "Total Draw Count", "paidDrawCount": "Paid Draw Count", "freeDrawCount": "Free Draw Count", @@ -29,6 +30,7 @@ "id": "ID", "playerUsername": "Player Username", "useCoins": "Use Coins", + "ante": "Ante", "totalDrawCount": "Total Draw Count", "paidDrawCount": "Paid Draw Count", "freeDrawCount": "Free Draw Count", diff --git a/saiadmin-artd/src/locales/langs/en/dice/reward.json b/saiadmin-artd/src/locales/langs/en/dice/reward.json index b9effe2..441702e 100644 --- a/saiadmin-artd/src/locales/langs/en/dice/reward.json +++ b/saiadmin-artd/src/locales/langs/en/dice/reward.json @@ -61,6 +61,7 @@ "stepFree": "Free ticket", "labelLotteryTypePaid": "Test pool type", "labelLotteryTypeFree": "Test pool type", + "labelAnte": "Ante", "placeholderPaidPool": "Leave empty for custom tier odds below (default: default)", "placeholderFreePool": "Leave empty for custom tier odds below (default: killScore)", "tierProbHint": "Custom tier odds (T1–T5), each 0–100%, sum of five must not exceed 100%", @@ -73,6 +74,7 @@ "btnNext": "Next", "btnStart": "Start test", "btnCancel": "Cancel", + "warnAnte": "Ante must be greater than 0", "warnTotalSpins": "At least one of paid/free direction spin counts must be greater than 0", "warnPaidTierSumPositive": "When no paid pool is selected, T1–T5 odds sum must be greater than 0", "warnPaidTierSumMax": "Paid T1–T5 odds sum cannot exceed 100%", diff --git a/saiadmin-artd/src/locales/langs/en/dice/reward_config_record.json b/saiadmin-artd/src/locales/langs/en/dice/reward_config_record.json index 57aaca8..ce6860e 100644 --- a/saiadmin-artd/src/locales/langs/en/dice/reward_config_record.json +++ b/saiadmin-artd/src/locales/langs/en/dice/reward_config_record.json @@ -12,6 +12,7 @@ "platformProfit": "Platform Profit", "totalDrawCount": "Total Draw Count", "createdBy": "Created By", + "remark": "Remark", "createTime": "Create Time", "statusFail": "Failed", "statusDone": "Done", diff --git a/saiadmin-artd/src/locales/langs/zh/dice/play_record.json b/saiadmin-artd/src/locales/langs/zh/dice/play_record.json index cdf41df..39a7319 100644 --- a/saiadmin-artd/src/locales/langs/zh/dice/play_record.json +++ b/saiadmin-artd/src/locales/langs/zh/dice/play_record.json @@ -8,7 +8,7 @@ "placeholderLotteryPool": "请选择彩金池配置", "drawType": "抽奖类型", "paid": "付费", - "free": "赠送", + "free": "免费", "isBigWin": "是否中大奖", "noBigWin": "无", "bigWin": "中大奖", @@ -53,7 +53,7 @@ "nameFuzzy": "名称模糊", "uiTextFuzzy": "前端显示文本模糊", "paid": "付费", - "free": "赠送", + "free": "免费", "noBigWin": "无", "bigWin": "中大奖", "clockwise": "顺时针", diff --git a/saiadmin-artd/src/locales/langs/zh/dice/play_record_test.json b/saiadmin-artd/src/locales/langs/zh/dice/play_record_test.json index 48c5f52..10556fa 100644 --- a/saiadmin-artd/src/locales/langs/zh/dice/play_record_test.json +++ b/saiadmin-artd/src/locales/langs/zh/dice/play_record_test.json @@ -8,10 +8,12 @@ "direction": "方向", "isBigWin": "是否中大奖", "winCoin": "赢取平台币", + "paidAmount": "付费金额", + "ante": "底注", "rewardTier": "奖励档位", "rollNumber": "摇取点数和", "paid": "付费", - "free": "赠送", + "free": "免费", "clockwise": "顺时针", "anticlockwise": "逆时针", "noBigWin": "无", @@ -23,6 +25,8 @@ "lotteryPoolConfig": "彩金池配置", "drawType": "抽奖类型", "isBigWin": "是否中大奖", + "paidAmount": "付费金额", + "ante": "底注", "winCoin": "赢取平台币", "superWinCoin": "中大奖平台币", "rewardWinCoin": "摇色子中奖平台币", diff --git a/saiadmin-artd/src/locales/langs/zh/dice/player_ticket_record.json b/saiadmin-artd/src/locales/langs/zh/dice/player_ticket_record.json index 7fb584a..4b98912 100644 --- a/saiadmin-artd/src/locales/langs/zh/dice/player_ticket_record.json +++ b/saiadmin-artd/src/locales/langs/zh/dice/player_ticket_record.json @@ -19,6 +19,7 @@ "search": { "player": "玩家", "useCoins": "消耗硬币", + "ante": "底注", "totalDrawCount": "总抽奖次数", "paidDrawCount": "购买抽奖次数", "freeDrawCount": "赠送抽奖次数", @@ -29,6 +30,7 @@ "id": "ID", "playerUsername": "玩家用户名", "useCoins": "消耗硬币", + "ante": "底注", "totalDrawCount": "总抽奖次数", "paidDrawCount": "购买抽奖次数", "freeDrawCount": "赠送抽奖次数", diff --git a/saiadmin-artd/src/locales/langs/zh/dice/reward.json b/saiadmin-artd/src/locales/langs/zh/dice/reward.json index 539b70b..dc6f85e 100644 --- a/saiadmin-artd/src/locales/langs/zh/dice/reward.json +++ b/saiadmin-artd/src/locales/langs/zh/dice/reward.json @@ -61,6 +61,7 @@ "stepFree": "免费抽奖券", "labelLotteryTypePaid": "测试数据档位类型", "labelLotteryTypeFree": "测试数据档位类型", + "labelAnte": "底注 ante", "placeholderPaidPool": "不选则下方自定义档位概率(默认 default)", "placeholderFreePool": "不选则下方自定义档位概率(默认 killScore)", "tierProbHint": "自定义档位概率(T1~T5),每档 0-100%,五档之和不能超过 100%", @@ -73,6 +74,7 @@ "btnNext": "下一步", "btnStart": "开始测试", "btnCancel": "取消", + "warnAnte": "底注 ante 必须大于 0", "warnTotalSpins": "付费或免费至少一种方向次数之和大于 0", "warnPaidTierSumPositive": "付费未选奖池时,T1~T5 档位概率之和需大于 0", "warnPaidTierSumMax": "付费档位概率 T1~T5 之和不能超过 100%", diff --git a/saiadmin-artd/src/locales/langs/zh/dice/reward_config_record.json b/saiadmin-artd/src/locales/langs/zh/dice/reward_config_record.json index 13b599e..f845f46 100644 --- a/saiadmin-artd/src/locales/langs/zh/dice/reward_config_record.json +++ b/saiadmin-artd/src/locales/langs/zh/dice/reward_config_record.json @@ -12,6 +12,7 @@ "platformProfit": "平台赚取金额", "totalDrawCount": "总抽奖次数", "createdBy": "创建管理员", + "remark": "备注", "createTime": "创建时间", "statusFail": "失败", "statusDone": "完成", diff --git a/saiadmin-artd/src/views/plugin/dice/api/reward/index.ts b/saiadmin-artd/src/views/plugin/dice/api/reward/index.ts index c24b104..b4bb150 100644 --- a/saiadmin-artd/src/views/plugin/dice/api/reward/index.ts +++ b/saiadmin-artd/src/views/plugin/dice/api/reward/index.ts @@ -61,6 +61,7 @@ export default { * 可选 lottery_config_id;不选则传 paid_tier_weights / free_tier_weights(T1-T5) */ startWeightTest(params: { + ante?: number lottery_config_id?: number paid_lottery_config_id?: number free_lottery_config_id?: number diff --git a/saiadmin-artd/src/views/plugin/dice/play_record_test/index/index.vue b/saiadmin-artd/src/views/plugin/dice/play_record_test/index/index.vue index a7b59d9..860fbed 100644 --- a/saiadmin-artd/src/views/plugin/dice/play_record_test/index/index.vue +++ b/saiadmin-artd/src/views/plugin/dice/play_record_test/index/index.vue @@ -58,19 +58,37 @@ @@ -132,6 +150,8 @@ lottery_type: undefined, direction: undefined, is_win: undefined, + paid_amount: undefined, + ante: undefined, win_coin_min: undefined, win_coin_max: undefined, reward_tier: undefined, @@ -208,9 +228,16 @@ columnsFactory: () => [ { type: 'selection' }, { prop: 'id', label: 'page.table.id', width: 80 }, - { prop: 'lottery_config_id', label: 'page.table.lotteryPoolConfig', width: 120, useSlot: true }, + { + prop: 'lottery_config_id', + label: 'page.table.lotteryPoolConfig', + width: 120, + useSlot: true + }, { prop: 'lottery_type', label: 'page.table.drawType', width: 100, useSlot: true }, { prop: 'is_win', label: 'page.table.isBigWin', width: 100, useSlot: true }, + { prop: 'paid_amount', label: 'page.table.paidAmount', width: 130 }, + { prop: 'ante', label: 'page.table.ante', width: 90 }, { prop: 'win_coin', label: 'page.table.winCoin', width: 110 }, { prop: 'super_win_coin', label: 'page.table.superWinCoin', width: 120 }, { prop: 'reward_win_coin', label: 'page.table.rewardWinCoin', width: 140 }, @@ -222,7 +249,13 @@ { prop: 'reward_config_id', label: 'page.table.rewardConfig', width: 100, useSlot: true }, { prop: 'status', label: 'page.table.status', width: 80, useSlot: true }, { prop: 'create_time', label: 'page.table.createTime', width: 170 }, - { prop: 'operation', label: 'table.actions.operation', width: 100, fixed: 'right', useSlot: true } + { + prop: 'operation', + label: 'table.actions.operation', + width: 100, + fixed: 'right', + useSlot: true + } ] } }) diff --git a/saiadmin-artd/src/views/plugin/dice/play_record_test/index/modules/edit-dialog.vue b/saiadmin-artd/src/views/plugin/dice/play_record_test/index/modules/edit-dialog.vue index b98f5a9..9fc76f5 100644 --- a/saiadmin-artd/src/views/plugin/dice/play_record_test/index/modules/edit-dialog.vue +++ b/saiadmin-artd/src/views/plugin/dice/play_record_test/index/modules/edit-dialog.vue @@ -28,6 +28,24 @@ + + + + + + @@ -153,6 +171,8 @@ lottery_config_id: null, lottery_type: null, is_win: null, + ante: 1, + paid_amount: 0, win_coin: 0, direction: null, reward_tier: undefined as string | undefined, diff --git a/saiadmin-artd/src/views/plugin/dice/play_record_test/index/modules/table-search.vue b/saiadmin-artd/src/views/plugin/dice/play_record_test/index/modules/table-search.vue index 373d666..ebac7e7 100644 --- a/saiadmin-artd/src/views/plugin/dice/play_record_test/index/modules/table-search.vue +++ b/saiadmin-artd/src/views/plugin/dice/play_record_test/index/modules/table-search.vue @@ -32,6 +32,28 @@ + + + + + + + + + +
diff --git a/saiadmin-artd/src/views/plugin/dice/player_ticket_record/index/index.vue b/saiadmin-artd/src/views/plugin/dice/player_ticket_record/index/index.vue index 9957acd..c408620 100644 --- a/saiadmin-artd/src/views/plugin/dice/player_ticket_record/index/index.vue +++ b/saiadmin-artd/src/views/plugin/dice/player_ticket_record/index/index.vue @@ -82,6 +82,7 @@ username: undefined, use_coins_min: undefined, use_coins_max: undefined, + ante: undefined, total_ticket_count_min: undefined, total_ticket_count_max: undefined, paid_ticket_count_min: undefined, @@ -136,6 +137,7 @@ formatter: (row: Record) => usernameFormatter(row) }, { prop: 'use_coins', label: 'page.table.useCoins', align: 'center' }, + { prop: 'ante', label: 'page.table.ante', align: 'center' }, { prop: 'total_ticket_count', label: 'page.table.totalDrawCount', align: 'center' }, { prop: 'paid_ticket_count', label: 'page.table.paidDrawCount', align: 'center' }, { prop: 'free_ticket_count', label: 'page.table.freeDrawCount', align: 'center' }, diff --git a/saiadmin-artd/src/views/plugin/dice/player_ticket_record/index/modules/table-search.vue b/saiadmin-artd/src/views/plugin/dice/player_ticket_record/index/modules/table-search.vue index 3bdf931..cc14c26 100644 --- a/saiadmin-artd/src/views/plugin/dice/player_ticket_record/index/modules/table-search.vue +++ b/saiadmin-artd/src/views/plugin/dice/player_ticket_record/index/modules/table-search.vue @@ -34,6 +34,18 @@
+ + + + +
diff --git a/saiadmin-artd/src/views/plugin/dice/reward/index/modules/weight-test-dialog.vue b/saiadmin-artd/src/views/plugin/dice/reward/index/modules/weight-test-dialog.vue index af3455a..a6ffbbc 100644 --- a/saiadmin-artd/src/views/plugin/dice/reward/index/modules/weight-test-dialog.vue +++ b/saiadmin-artd/src/views/plugin/dice/reward/index/modules/weight-test-dialog.vue @@ -12,6 +12,9 @@ {{ $t('page.weightTest.alertBody') }} + + + @@ -187,6 +190,7 @@ const formRef = ref() const currentStep = ref(0) const form = reactive({ + ante: 1, paid_lottery_config_id: undefined as number | undefined, free_lottery_config_id: undefined as number | undefined, paid_tier_weights: { T1: 20, T2: 20, T3: 20, T4: 20, T5: 20 } as Record, @@ -270,6 +274,7 @@ function buildPayload() { const payload: Record = { + ante: form.ante, paid_s_count: form.paid_s_count, paid_n_count: form.paid_n_count, free_s_count: form.free_s_count, @@ -289,6 +294,10 @@ } function validateForm(): boolean { + if (form.ante == null || form.ante <= 0) { + ElMessage.warning(t('page.weightTest.warnAnte')) + return false + } if (form.paid_s_count + form.paid_n_count + form.free_s_count + form.free_n_count <= 0) { ElMessage.warning(t('page.weightTest.warnTotalSpins')) return false diff --git a/saiadmin-artd/src/views/plugin/dice/reward_config_record/index/index.vue b/saiadmin-artd/src/views/plugin/dice/reward_config_record/index/index.vue index 2fe8e60..ac74471 100644 --- a/saiadmin-artd/src/views/plugin/dice/reward_config_record/index/index.vue +++ b/saiadmin-artd/src/views/plugin/dice/reward_config_record/index/index.vue @@ -214,6 +214,13 @@ align: 'center', showOverflowTooltip: true }, + { + prop: 'remark', + label: 'page.table.remark', + width: 220, + align: 'center', + showOverflowTooltip: true + }, { prop: 'create_time', label: 'page.table.createTime', width: 170, align: 'center' }, { prop: 'operation', diff --git a/server/app/api/logic/PlayStartLogic.php b/server/app/api/logic/PlayStartLogic.php index 5a10c61..0f01f66 100644 --- a/server/app/api/logic/PlayStartLogic.php +++ b/server/app/api/logic/PlayStartLogic.php @@ -284,6 +284,21 @@ class PlayStartLogic $p->free_ticket_count = max(0, (int) $p->free_ticket_count - 1); } + // 记录每次游玩:写入抽奖券记录(用于后台“抽奖券记录”追踪付费/免费游玩与消耗) + $isPaidPlay = $ticketType === self::LOTTERY_TYPE_PAID; + $paidCnt = $isPaidPlay ? 1 : 0; + $freeCnt = $isPaidPlay ? 0 : 1; + DicePlayerTicketRecord::create([ + 'player_id' => $playerId, + 'admin_id' => $adminId, + 'use_coins' => $paidAmount, + 'ante' => $ante, + 'total_ticket_count' => $paidCnt + $freeCnt, + 'paid_ticket_count' => $paidCnt, + 'free_ticket_count' => $freeCnt, + 'remark' => ($isPaidPlay ? '付费游玩' : '免费游玩') . '|play_record_id=' . $record->id, + ]); + // 若本局中奖档位为 T5,则额外赠送 1 次免费抽奖次数(总次数也 +1),并记录抽奖券获取记录 if ($isTierT5) { $p->free_ticket_count = (int) $p->free_ticket_count + 1; @@ -509,10 +524,11 @@ class PlayStartLogic * @param \app\dice\model\lottery_pool_config\DiceLotteryPoolConfig|null $config 奖池配置,自定义档位时可为 null * @param int $direction 0=顺时针 1=逆时针 * @param int $lotteryType 0=付费 1=免费 + * @param int $ante 底注/注数(dice_ante_config.mult) * @param array|null $customTierWeights 自定义档位权重 ['T1'=>x, 'T2'=>x, ...],非空时忽略 config 的档位权重 * @return array 可直接用于 DicePlayRecordTest::create 的字段 + tier(用于统计档位概率) */ - public function simulateOnePlay($config, int $direction, int $lotteryType = 0, ?array $customTierWeights = null): array + public function simulateOnePlay($config, int $direction, int $lotteryType = 0, int $ante = 1, ?array $customTierWeights = null): array { $rewardInstance = DiceReward::getCachedInstance(); $byTierDirection = $rewardInstance['by_tier_direction'] ?? []; @@ -558,8 +574,8 @@ class PlayStartLogic $targetIndex = (int) ($chosen['end_index'] ?? 0); $rollNumber = (int) ($chosen['grid_number'] ?? 0); $realEv = (float) ($chosen['real_ev'] ?? 0); - $isTierT5 = (string) ($chosen['tier'] ?? '') === 'T5'; - $rewardWinCoin = $isTierT5 ? $realEv : (100 + $realEv); + // 玩家始终增加:(100 + real_ev) * ante + $rewardWinCoin = (self::UNIT_COST + $realEv) * $ante; $superWinCoin = 0; $isWin = 0; @@ -588,8 +604,11 @@ class PlayStartLogic if ($doSuperWin) { $rollArray = $this->getSuperWinRollArray($rollNumber); $isWin = 1; - $superWinCoin = $bigWinRealEv > 0 ? $bigWinRealEv : self::SUPER_WIN_BONUS; + $bigWinEv = $bigWinRealEv > 0 ? $bigWinRealEv : self::SUPER_WIN_BONUS; + $superWinCoin = (self::UNIT_COST + $bigWinEv) * $ante; $rewardWinCoin = 0; + // 中豹子时不走原奖励流程 + $realEv = 0.0; } else { $rollArray = $this->generateNonSuperWinRollArrayWithSum($rollNumber); } @@ -603,6 +622,7 @@ class PlayStartLogic $rewardId = ($isWin === 1 && $superWinCoin > 0) ? 0 : $targetIndex; $configName = $config !== null ? (string) ($config->name ?? '') : '自定义'; $costRealEv = $realEv + ($isWin === 1 ? $bigWinRealEv : 0.0); + $paidAmount = $lotteryType === 0 ? ($ante * self::UNIT_COST) : 0; return [ 'player_id' => 0, @@ -611,9 +631,11 @@ class PlayStartLogic 'lottery_type' => $lotteryType, 'is_win' => $isWin, 'win_coin' => $winCoin, + 'ante' => $ante, + 'paid_amount' => $paidAmount, 'super_win_coin' => $superWinCoin, 'reward_win_coin' => $rewardWinCoin, - 'use_coins' => 0, + 'use_coins' => $paidAmount, 'direction' => $direction, 'reward_config_id' => $rewardId, 'start_index' => $startIndex, diff --git a/server/app/dice/controller/play_record_test/DicePlayRecordTestController.php b/server/app/dice/controller/play_record_test/DicePlayRecordTestController.php index 5c325be..fbfdde8 100644 --- a/server/app/dice/controller/play_record_test/DicePlayRecordTestController.php +++ b/server/app/dice/controller/play_record_test/DicePlayRecordTestController.php @@ -43,18 +43,20 @@ class DicePlayRecordTestController extends BaseController ['is_win', ''], ['win_coin_min', ''], ['win_coin_max', ''], + ['paid_amount', ''], + ['ante', ''], ['reward_tier', ''], ['roll_number', ''], ]); $query = $this->logic->search($where); $query->with(['diceLotteryPoolConfig', 'diceRewardConfig']); - // 按当前筛选条件统计:平台总盈利 = 付费抽奖(lottery_type=0)次数×100 - 玩家总收益(win_coin 求和) + // 按当前筛选条件统计:平台总盈利 = 付费金额(paid_amount 求和) - 玩家总收益(win_coin 求和) $sumQuery = clone $query; $playerTotalWin = (float) $sumQuery->sum('win_coin'); - $paidCountQuery = clone $query; - $paidCount = (int) $paidCountQuery->where('lottery_type', 0)->count(); - $totalWinCoin = $paidCount * 100 - $playerTotalWin; + $paidAmountQuery = clone $query; + $paidAmount = (float) $paidAmountQuery->where('lottery_type', 0)->sum('paid_amount'); + $totalWinCoin = $paidAmount - $playerTotalWin; $data = $this->logic->getList($query); $data['total_win_coin'] = $totalWinCoin; diff --git a/server/app/dice/controller/player_ticket_record/DicePlayerTicketRecordController.php b/server/app/dice/controller/player_ticket_record/DicePlayerTicketRecordController.php index c5376d7..5052e6d 100644 --- a/server/app/dice/controller/player_ticket_record/DicePlayerTicketRecordController.php +++ b/server/app/dice/controller/player_ticket_record/DicePlayerTicketRecordController.php @@ -42,6 +42,7 @@ class DicePlayerTicketRecordController extends BaseController ['username', ''], ['use_coins_min', ''], ['use_coins_max', ''], + ['ante', ''], ['total_ticket_count_min', ''], ['total_ticket_count_max', ''], ['paid_ticket_count_min', ''], diff --git a/server/app/dice/controller/reward/DiceRewardController.php b/server/app/dice/controller/reward/DiceRewardController.php index 39cc740..81d6efb 100644 --- a/server/app/dice/controller/reward/DiceRewardController.php +++ b/server/app/dice/controller/reward/DiceRewardController.php @@ -90,6 +90,7 @@ class DiceRewardController extends BaseController { $post = is_array($request->post()) ? $request->post() : []; $params = [ + 'ante' => $post['ante'] ?? null, 'lottery_config_id' => $post['lottery_config_id'] ?? null, 'paid_lottery_config_id' => $post['paid_lottery_config_id'] ?? null, 'free_lottery_config_id' => $post['free_lottery_config_id'] ?? null, diff --git a/server/app/dice/logic/reward_config_record/DiceRewardConfigRecordLogic.php b/server/app/dice/logic/reward_config_record/DiceRewardConfigRecordLogic.php index c1365b2..a531fa0 100644 --- a/server/app/dice/logic/reward_config_record/DiceRewardConfigRecordLogic.php +++ b/server/app/dice/logic/reward_config_record/DiceRewardConfigRecordLogic.php @@ -5,6 +5,7 @@ namespace app\dice\logic\reward_config_record; use app\dice\model\lottery_pool_config\DiceLotteryPoolConfig; +use app\dice\model\ante_config\DiceAnteConfig; use app\dice\model\reward\DiceReward; use app\dice\model\reward\DiceRewardConfig; use app\dice\model\reward_config_record\DiceRewardConfigRecord; @@ -250,6 +251,15 @@ class DiceRewardConfigRecordLogic extends BaseLogic $adminId = $adminIdOrFreeS !== null && $adminIdOrFreeS !== '' ? (int) $adminIdOrFreeS : null; } $allowed = [100, 500, 1000, 5000]; + $ante = isset($params['ante']) ? intval($params['ante']) : 1; + if ($ante <= 0) { + throw new ApiException('ante must be greater than 0'); + } + $anteExists = DiceAnteConfig::where('mult', $ante)->count(); + if ($anteExists <= 0) { + throw new ApiException('ante not allowed: ' . $ante); + } + $lotteryConfigId = isset($params['lottery_config_id']) ? (int) $params['lottery_config_id'] : 0; $paidConfigId = isset($params['paid_lottery_config_id']) ? (int) $params['paid_lottery_config_id'] : 0; $freeConfigId = isset($params['free_lottery_config_id']) ? (int) $params['free_lottery_config_id'] : 0; @@ -407,6 +417,7 @@ class DiceRewardConfigRecordLogic extends BaseLogic $record->result_counts = []; $record->tier_counts = null; $record->bigwin_weight = $bigwinWeights ?: null; + $record->ante = $ante; $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 5a28042..abbe5b5 100644 --- a/server/app/dice/logic/reward_config_record/WeightTestRunner.php +++ b/server/app/dice/logic/reward_config_record/WeightTestRunner.php @@ -33,6 +33,7 @@ class WeightTestRunner return; } + $ante = is_numeric($record->ante ?? null) ? intval($record->ante) : 1; $paidS = (int) ($record->paid_s_count ?? 0); $paidN = (int) ($record->paid_n_count ?? 0); $freeS = (int) ($record->free_s_count ?? 0); @@ -60,28 +61,40 @@ class WeightTestRunner $safetyLine = (int) ($configType0->safety_line ?? 0); $killEnabled = ((int) ($configType0->kill_enabled ?? 1)) === 1; - $paidTierWeights = (is_array($record->paid_tier_weights ?? null) && $record->paid_tier_weights !== []) + $paidTierWeightsCustom = (is_array($record->paid_tier_weights ?? null) && $record->paid_tier_weights !== []) ? $record->paid_tier_weights - : [ - 'T1' => (int) ($configType0->t1_weight ?? 0), - 'T2' => (int) ($configType0->t2_weight ?? 0), - 'T3' => (int) ($configType0->t3_weight ?? 0), - 'T4' => (int) ($configType0->t4_weight ?? 0), - 'T5' => (int) ($configType0->t5_weight ?? 0), - ]; - if (array_sum($paidTierWeights) <= 0) { - $this->markFailed($recordId, '需提供 paid_tier_weights(玩家权重,盈利未达安全线时付费抽奖使用)或选择 default 奖池'); - return; + : null; + $freeTierWeightsCustom = (is_array($record->free_tier_weights ?? null) && $record->free_tier_weights !== []) + ? $record->free_tier_weights + : null; + + $paidPoolConfigId = (int) ($record->paid_lottery_config_id ?? 0); + $freePoolConfigId = (int) ($record->free_lottery_config_id ?? 0); + + $paidPoolConfig = $paidPoolConfigId > 0 ? DiceLotteryPoolConfig::find($paidPoolConfigId) : $configType0; + if (!$paidPoolConfig) { + $paidPoolConfig = $configType0; + } + $freePoolConfig = $freePoolConfigId > 0 ? DiceLotteryPoolConfig::find($freePoolConfigId) : $configType1; + if (!$freePoolConfig) { + $freePoolConfig = $configType0; } - $freeConfig = $configType1 !== null ? $configType1 : $configType0; + if ($paidTierWeightsCustom !== null && array_sum($paidTierWeightsCustom) <= 0) { + $this->markFailed($recordId, 'paid_tier_weights(玩家权重)之和必须大于 0'); + return; + } + if ($freeTierWeightsCustom !== null && array_sum($freeTierWeightsCustom) <= 0) { + $this->markFailed($recordId, 'free_tier_weights(免费玩家权重)之和必须大于 0'); + return; + } // 每次测试开始前清空进程内静态缓存,强制从共享缓存读取最新 BIGWIN/奖励配置,与数据库一致 DiceRewardConfig::clearRequestInstance(); DiceReward::clearRequestInstance(); // 彩金池累计盈利:用于判断是否触发杀分(不再依赖单个玩家累计盈利) - $poolProfitTotal = $configType0->profit_amount ?? 0; + $poolProfitTotal = floatval($configType0->profit_amount ?? 0); $playLogic = new PlayStartLogic(); $resultCounts = []; @@ -92,9 +105,9 @@ class WeightTestRunner try { for ($i = 0; $i < $paidS; $i++) { $usePoolWeights = $killEnabled && $poolProfitTotal >= $safetyLine && $configType1 !== null; - $paidConfig = $usePoolWeights ? $configType1 : $configType0; - $customWeights = $usePoolWeights ? null : $paidTierWeights; - $row = $playLogic->simulateOnePlay($paidConfig, 0, 0, $customWeights); + $paidConfig = $usePoolWeights ? $configType1 : $paidPoolConfig; + $customWeights = $usePoolWeights ? null : $paidTierWeightsCustom; + $row = $playLogic->simulateOnePlay($paidConfig, 0, 0, $ante, $customWeights); $this->accumulateProfitForDefault($row, 0, $paidConfig, $configType0, $poolProfitTotal); $this->aggregate($row, $resultCounts, $tierCounts); $buffer[] = $this->rowForInsert($row, $recordId); @@ -103,9 +116,9 @@ class WeightTestRunner } for ($i = 0; $i < $paidN; $i++) { $usePoolWeights = $killEnabled && $poolProfitTotal >= $safetyLine && $configType1 !== null; - $paidConfig = $usePoolWeights ? $configType1 : $configType0; - $customWeights = $usePoolWeights ? null : $paidTierWeights; - $row = $playLogic->simulateOnePlay($paidConfig, 1, 0, $customWeights); + $paidConfig = $usePoolWeights ? $configType1 : $paidPoolConfig; + $customWeights = $usePoolWeights ? null : $paidTierWeightsCustom; + $row = $playLogic->simulateOnePlay($paidConfig, 1, 0, $ante, $customWeights); $this->accumulateProfitForDefault($row, 0, $paidConfig, $configType0, $poolProfitTotal); $this->aggregate($row, $resultCounts, $tierCounts); $buffer[] = $this->rowForInsert($row, $recordId); @@ -113,7 +126,10 @@ class WeightTestRunner $this->flushIfNeeded($buffer, $recordId, $done, $total, $resultCounts, $tierCounts); } for ($i = 0; $i < $freeS; $i++) { - $row = $playLogic->simulateOnePlay($freeConfig, 0, 1, null); + $useKillMode = $killEnabled && $poolProfitTotal >= $safetyLine && $configType1 !== null; + $freeConfig = $useKillMode ? $configType1 : $freePoolConfig; + $customWeights = $useKillMode ? null : $freeTierWeightsCustom; + $row = $playLogic->simulateOnePlay($freeConfig, 0, 1, $ante, $customWeights); $this->accumulateProfitForDefault($row, 1, $freeConfig, $configType0, $poolProfitTotal); $this->aggregate($row, $resultCounts, $tierCounts); $buffer[] = $this->rowForInsert($row, $recordId); @@ -121,7 +137,10 @@ class WeightTestRunner $this->flushIfNeeded($buffer, $recordId, $done, $total, $resultCounts, $tierCounts); } for ($i = 0; $i < $freeN; $i++) { - $row = $playLogic->simulateOnePlay($freeConfig, 1, 1, null); + $useKillMode = $killEnabled && $poolProfitTotal >= $safetyLine && $configType1 !== null; + $freeConfig = $useKillMode ? $configType1 : $freePoolConfig; + $customWeights = $useKillMode ? null : $freeTierWeightsCustom; + $row = $playLogic->simulateOnePlay($freeConfig, 1, 1, $ante, $customWeights); $this->accumulateProfitForDefault($row, 1, $freeConfig, $configType0, $poolProfitTotal); $this->aggregate($row, $resultCounts, $tierCounts); $buffer[] = $this->rowForInsert($row, $recordId); @@ -153,7 +172,8 @@ class WeightTestRunner return; } $winCoin = (float) $row['win_coin']; - $playerProfitTotal += $lotteryType === 0 ? ($winCoin - 100.0) : $winCoin; + $paidAmount = (float) ($row['paid_amount'] ?? 0); + $playerProfitTotal += $lotteryType === 0 ? ($winCoin - $paidAmount) : $winCoin; } private function aggregate(array $row, array &$resultCounts, array &$tierCounts): void @@ -176,6 +196,7 @@ class WeightTestRunner $keys = [ 'player_id', 'admin_id', 'lottery_config_id', 'lottery_type', 'is_win', 'win_coin', 'super_win_coin', 'reward_win_coin', 'use_coins', 'direction', 'reward_config_id', + 'ante', 'paid_amount', 'start_index', 'target_index', 'roll_array', 'roll_number', 'lottery_name', 'status', ]; foreach ($keys as $k) { @@ -219,7 +240,7 @@ class WeightTestRunner /** * 标记测试成功并记录平台总盈利 platform_profit - * 通过关联 DicePlayRecordTest(reward_config_record_id)统计:付费(lottery_type=0)次数×100 - win_coin 求和 + * 通过关联 DicePlayRecordTest(reward_config_record_id)统计:付费金额 paid_amount 求和 - win_coin 求和 */ private function markSuccess(int $recordId, array $resultCounts, array $tierCounts): void { diff --git a/server/app/dice/model/play_record_test/DicePlayRecordTest.php b/server/app/dice/model/play_record_test/DicePlayRecordTest.php index cf9025d..0a8ed5b 100644 --- a/server/app/dice/model/play_record_test/DicePlayRecordTest.php +++ b/server/app/dice/model/play_record_test/DicePlayRecordTest.php @@ -19,9 +19,11 @@ use think\model\relation\BelongsTo; * * @property $id ID * @property $lottery_config_id 彩金池配置id - * @property $lottery_type 抽奖类型:0=付费,1=赠送 + * @property $lottery_type 抽奖类型:0=付费,1=免费 * @property $is_win 中大奖:0=无,1=中奖 * @property $win_coin 赢取平台币 + * @property int|null $ante 底注/注数(dice_ante_config.mult) + * @property int|null $paid_amount 付费金额(付费局=ante*100,免费局=0) * @property $direction 方向:0=顺时针,1=逆时针 * @property $reward_config_id 奖励配置id * @property $create_time 创建时间 @@ -77,7 +79,7 @@ class DicePlayRecordTest extends BaseModel return $this->belongsTo(DiceRewardConfigRecord::class, 'reward_config_record_id', 'id'); } - /** 抽奖类型 0=付费 1=赠送 */ + /** 抽奖类型 0=付费 1=免费 */ public function searchLotteryTypeAttr($query, $value) { if ($value !== '' && $value !== null) { @@ -117,6 +119,22 @@ class DicePlayRecordTest extends BaseModel } } + /** 付费金额(付费局=ante*100,免费局=0) */ + public function searchPaidAmountAttr($query, $value) + { + if ($value !== '' && $value !== null) { + $query->where('paid_amount', '=', $value); + } + } + + /** 底注/注数(dice_ante_config.mult) */ + public function searchAnteAttr($query, $value) + { + if ($value !== '' && $value !== null) { + $query->where('ante', '=', $value); + } + } + /** 中奖档位(按 reward_config_id 对应 DiceRewardConfig.tier) */ public function searchRewardTierAttr($query, $value) { diff --git a/server/app/dice/model/player_ticket_record/DicePlayerTicketRecord.php b/server/app/dice/model/player_ticket_record/DicePlayerTicketRecord.php index 5ba3822..292c73d 100644 --- a/server/app/dice/model/player_ticket_record/DicePlayerTicketRecord.php +++ b/server/app/dice/model/player_ticket_record/DicePlayerTicketRecord.php @@ -144,4 +144,12 @@ class DicePlayerTicketRecord extends BaseModel $query->where('create_time', '<=', $value); } } + + /** 底注/注数(ante) */ + public function searchAnteAttr($query, $value) + { + if ($value !== '' && $value !== null) { + $query->where('ante', '=', $value); + } + } } diff --git a/server/app/dice/model/reward_config_record/DiceRewardConfigRecord.php b/server/app/dice/model/reward_config_record/DiceRewardConfigRecord.php index 6fa0da1..7a464bc 100644 --- a/server/app/dice/model/reward_config_record/DiceRewardConfigRecord.php +++ b/server/app/dice/model/reward_config_record/DiceRewardConfigRecord.php @@ -26,6 +26,7 @@ use think\model\relation\HasMany; * @property int $over_play_count 已完成次数 * @property int $status 状态 -1失败 0进行中 1成功 * @property string|null $remark 失败时记录原因 + * @property int|null $ante 底注/注数(dice_ante_config.mult) * @property int $s_count 顺时针模拟次数(兼容旧数据) * @property int $n_count 逆时针模拟次数(兼容旧数据) * @property int $paid_s_count 付费抽奖顺时针次数 @@ -70,18 +71,18 @@ class DiceRewardConfigRecord extends BaseModel /** * 根据关联的 DicePlayRecordTest 统计平台赚取平台币 - * platform_profit = 关联的付费(lottery_type=0)抽取次数 × 100 - 关联的 win_coin 求和 + * platform_profit = 关联的付费(lottery_type=0)付费金额求和(paid_amount) - 关联的 win_coin 求和 * @param int $recordId dice_reward_config_record.id * @return float */ public static function computePlatformProfitFromRelated(int $recordId): float { - $paidCount = DicePlayRecordTest::where('reward_config_record_id', $recordId) + $paidAmount = (float) DicePlayRecordTest::where('reward_config_record_id', $recordId) ->where('lottery_type', 0) - ->count(); + ->sum('paid_amount'); $sumWinCoin = (float) DicePlayRecordTest::where('reward_config_record_id', $recordId) ->sum('win_coin'); - return round($paidCount * 100 - $sumWinCoin, 2); + return round($paidAmount - $sumWinCoin, 2); } /** diff --git a/server/app/dice/validate/play_record_test/DicePlayRecordTestValidate.php b/server/app/dice/validate/play_record_test/DicePlayRecordTestValidate.php index 7426aba..b14cefa 100644 --- a/server/app/dice/validate/play_record_test/DicePlayRecordTestValidate.php +++ b/server/app/dice/validate/play_record_test/DicePlayRecordTestValidate.php @@ -30,7 +30,7 @@ class DicePlayRecordTestValidate extends BaseValidate */ protected $message = [ 'lottery_config_id' => '彩金池配置id必须填写', - 'lottery_type' => '抽奖类型:0=付费,1=赠送必须填写', + 'lottery_type' => '抽奖类型:0=付费,1=免费必须填写', 'is_win' => '中大奖:0=无,1=中奖必须填写', 'direction' => '方向:0=顺时针,1=逆时针必须填写', 'reward_config_id' => '奖励配置id必须填写',