优化一键测试权重
This commit is contained in:
@@ -81,9 +81,10 @@ class DiceRewardController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* 一键测试权重:创建测试记录并启动单进程后台执行,按付费/免费、顺逆方向交替写入 dice_play_record_test
|
||||
* 参数:lottery_config_id 可选,不选则传 paid_tier_weights / free_tier_weights 自定义档位;
|
||||
* paid_s_count, paid_n_count, free_s_count, free_n_count;或兼容旧版 s_count, n_count
|
||||
* 一键测试权重:创建测试记录并启动单进程后台执行,写入 dice_play_record_test
|
||||
* 参数:lottery_config_id 可选;paid_tier_weights / free_tier_weights 自定义档位;
|
||||
* paid_s_count, paid_n_count
|
||||
* chain_free_mode=1:仅按付费次数模拟;付费抽到再来一次/T5 则在队列中插入免费局(同底注、lottery_type=免费、paid_amount=0)
|
||||
*/
|
||||
#[Permission('一键测试权重', 'dice:reward:index:startWeightTest')]
|
||||
public function startWeightTest(Request $request): Response
|
||||
@@ -94,14 +95,11 @@ class DiceRewardController extends BaseController
|
||||
'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,
|
||||
's_count' => $post['s_count'] ?? null,
|
||||
'n_count' => $post['n_count'] ?? null,
|
||||
'paid_s_count' => $post['paid_s_count'] ?? null,
|
||||
'paid_n_count' => $post['paid_n_count'] ?? null,
|
||||
'free_s_count' => $post['free_s_count'] ?? null,
|
||||
'free_n_count' => $post['free_n_count'] ?? null,
|
||||
'paid_tier_weights' => $post['paid_tier_weights'] ?? null,
|
||||
'free_tier_weights' => $post['free_tier_weights'] ?? null,
|
||||
'chain_free_mode' => $post['chain_free_mode'] ?? null,
|
||||
];
|
||||
$adminId = isset($this->adminInfo['id']) ? (int) $this->adminInfo['id'] : null;
|
||||
try {
|
||||
|
||||
@@ -229,10 +229,10 @@ class DiceRewardConfigRecordLogic extends BaseLogic
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一键测试权重记录并返回 ID,供后台执行器按付费/免费、顺逆方向交替写入 dice_play_record_test
|
||||
* 创建一键测试权重记录并返回 ID,供后台执行器写入 dice_play_record_test
|
||||
* 支持两种模式:1)选择奖池配置 lottery_config_id,档位概率取自配置;2)不选配置,使用自定义 paid_tier_weights / free_tier_weights
|
||||
* @param array|int $params 数组:lottery_config_id(可选), paid_s_count, paid_n_count, free_s_count, free_n_count;或兼容旧版传 4 个 int 时视为 (paid_s_count, paid_n_count, free_s_count, free_n_count)
|
||||
* @param int|null $adminId 执行人(旧版 4 参调用时第二参为 paid_n_count,此处不传 adminId)
|
||||
* @param array|int $params 数组:lottery_config_id(可选), paid_s_count, paid_n_count
|
||||
* @param int|null $adminId 执行人
|
||||
* @return int 记录 ID
|
||||
* @throws ApiException
|
||||
*/
|
||||
@@ -240,12 +240,10 @@ class DiceRewardConfigRecordLogic extends BaseLogic
|
||||
{
|
||||
$adminId = null;
|
||||
if (!is_array($params)) {
|
||||
// 兼容旧版调用:createWeightTestRecord(paid_s_count, paid_n_count, free_s_count, free_n_count)
|
||||
// 兼容旧版调用:createWeightTestRecord(paid_s_count, paid_n_count)
|
||||
$params = [
|
||||
'paid_s_count' => (int) $params,
|
||||
'paid_n_count' => (int) $adminIdOrFreeS,
|
||||
'free_s_count' => (int) $freeSOrFreeN,
|
||||
'free_n_count' => (int) $freeN,
|
||||
];
|
||||
} else {
|
||||
$adminId = $adminIdOrFreeS !== null && $adminIdOrFreeS !== '' ? (int) $adminIdOrFreeS : null;
|
||||
@@ -269,19 +267,18 @@ class DiceRewardConfigRecordLogic extends BaseLogic
|
||||
if ($freeConfigId <= 0 && $lotteryConfigId > 0) {
|
||||
$freeConfigId = $lotteryConfigId;
|
||||
}
|
||||
$paidS = isset($params['paid_s_count']) ? (int) $params['paid_s_count'] : (int) ($params['s_count'] ?? 0);
|
||||
$paidN = isset($params['paid_n_count']) ? (int) $params['paid_n_count'] : (int) ($params['n_count'] ?? 0);
|
||||
$freeS = (int) ($params['free_s_count'] ?? 0);
|
||||
$freeN = (int) ($params['free_n_count'] ?? 0);
|
||||
$paidS = isset($params['paid_s_count']) ? (int) $params['paid_s_count'] : 0;
|
||||
$paidN = isset($params['paid_n_count']) ? (int) $params['paid_n_count'] : 0;
|
||||
$chainFreeMode = !empty($params['chain_free_mode']);
|
||||
|
||||
foreach ([$paidS, $paidN, $freeS, $freeN] as $c) {
|
||||
foreach ([$paidS, $paidN] as $c) {
|
||||
if ($c !== 0 && !in_array($c, $allowed, true)) {
|
||||
throw new ApiException('Counts only support 0, 100, 500, 1000, 5000');
|
||||
}
|
||||
}
|
||||
$total = $paidS + $paidN + $freeS + $freeN;
|
||||
$total = $paidS + $paidN;
|
||||
if ($total <= 0) {
|
||||
throw new ApiException('Sum of paid/free direction counts must be greater than 0');
|
||||
throw new ApiException('Sum of paid direction counts must be greater than 0');
|
||||
}
|
||||
|
||||
$snapshot = [];
|
||||
@@ -394,24 +391,28 @@ class DiceRewardConfigRecordLogic extends BaseLogic
|
||||
if (!is_array($tierWeightsSnapshot['free'])) {
|
||||
$tierWeightsSnapshot['free'] = [];
|
||||
}
|
||||
if ($chainFreeMode) {
|
||||
$tierWeightsSnapshot['chain_free_mode'] = true;
|
||||
}
|
||||
|
||||
$record = new DiceRewardConfigRecord();
|
||||
$record->test_count = $total;
|
||||
$plannedPaidSpins = $paidS + $paidN;
|
||||
$record->chain_free_mode = $chainFreeMode ? 1 : 0;
|
||||
$record->paid_planned_spins = $plannedPaidSpins;
|
||||
// 总抽奖次数与 test_count 仅在任务成功结束时写入(见 WeightTestRunner::markSuccess)
|
||||
$record->test_count = 0;
|
||||
$record->total_play_count = 0;
|
||||
$record->weight_config_snapshot = $snapshot;
|
||||
$record->tier_weights_snapshot = $tierWeightsSnapshot;
|
||||
$record->lottery_config_id = $lotteryConfigId > 0 ? $lotteryConfigId : null;
|
||||
$record->paid_lottery_config_id = $paidConfigId > 0 ? $paidConfigId : null;
|
||||
$record->free_lottery_config_id = $freeConfigId > 0 ? $freeConfigId : null;
|
||||
$record->total_play_count = $total;
|
||||
$record->over_play_count = 0;
|
||||
$record->status = DiceRewardConfigRecord::STATUS_RUNNING;
|
||||
$record->remark = null;
|
||||
$record->s_count = $paidS + $paidN;
|
||||
$record->n_count = $freeS + $freeN;
|
||||
$record->paid_s_count = $paidS;
|
||||
$record->paid_n_count = $paidN;
|
||||
$record->free_s_count = $freeS;
|
||||
$record->free_n_count = $freeN;
|
||||
$record->play_again_count = 0;
|
||||
$record->paid_tier_weights = $paidTierWeights;
|
||||
$record->free_tier_weights = $freeTierWeights;
|
||||
$record->result_counts = [];
|
||||
|
||||
@@ -56,20 +56,10 @@ class WeightTestRunner
|
||||
$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);
|
||||
$freeN = (int) ($record->free_n_count ?? 0);
|
||||
if ($paidS + $paidN + $freeS + $freeN <= 0) {
|
||||
$sCount = (int) ($record->s_count ?? 0);
|
||||
$nCount = (int) ($record->n_count ?? 0);
|
||||
$total = $sCount + $nCount;
|
||||
if ($total <= 0) {
|
||||
$this->markFailed($recordId, '抽奖次数必须大于 0');
|
||||
return;
|
||||
}
|
||||
$paidS = $sCount;
|
||||
$paidN = $nCount;
|
||||
} else {
|
||||
$total = $paidS + $paidN + $freeS + $freeN;
|
||||
$total = $paidS + $paidN;
|
||||
if ($total <= 0) {
|
||||
$this->markFailed($recordId, '抽奖次数必须大于 0');
|
||||
return;
|
||||
}
|
||||
|
||||
$configType0 = DiceLotteryPoolConfig::where('name', 'default')->find();
|
||||
@@ -123,53 +113,30 @@ class WeightTestRunner
|
||||
$done = 0;
|
||||
|
||||
try {
|
||||
for ($i = 0; $i < $paidS; $i++) {
|
||||
$usePoolWeights = $killEnabled && $poolProfitTotal >= $safetyLine && $configType1 !== null;
|
||||
$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);
|
||||
$done++;
|
||||
$this->flushIfNeeded($buffer, $recordId, $done, $total, $resultCounts, $tierCounts);
|
||||
}
|
||||
for ($i = 0; $i < $paidN; $i++) {
|
||||
$usePoolWeights = $killEnabled && $poolProfitTotal >= $safetyLine && $configType1 !== null;
|
||||
$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);
|
||||
$done++;
|
||||
$this->flushIfNeeded($buffer, $recordId, $done, $total, $resultCounts, $tierCounts);
|
||||
}
|
||||
for ($i = 0; $i < $freeS; $i++) {
|
||||
$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);
|
||||
$done++;
|
||||
$this->flushIfNeeded($buffer, $recordId, $done, $total, $resultCounts, $tierCounts);
|
||||
}
|
||||
for ($i = 0; $i < $freeN; $i++) {
|
||||
$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);
|
||||
$done++;
|
||||
$this->flushIfNeeded($buffer, $recordId, $done, $total, $resultCounts, $tierCounts);
|
||||
}
|
||||
$this->runChainFreeMode(
|
||||
$recordId,
|
||||
$playLogic,
|
||||
$paidS,
|
||||
$paidN,
|
||||
$ante,
|
||||
$paidPoolConfig,
|
||||
$freePoolConfig,
|
||||
$paidTierWeightsCustom,
|
||||
$freeTierWeightsCustom,
|
||||
$configType0,
|
||||
$configType1,
|
||||
$safetyLine,
|
||||
$killEnabled,
|
||||
$poolProfitTotal,
|
||||
$resultCounts,
|
||||
$tierCounts,
|
||||
$buffer,
|
||||
$done
|
||||
);
|
||||
if (!empty($buffer)) {
|
||||
$this->insertBuffer($buffer);
|
||||
$this->updateProgress($recordId, $done, $resultCounts, $tierCounts);
|
||||
// 链式/非链式:运行中均不写入 total_play_count,仅在 markSuccess 落库实际总次数
|
||||
$this->updateProgress($recordId, $done, $resultCounts, $tierCounts, null);
|
||||
}
|
||||
// 平台赚取金额:通过关联 DicePlayRecordTest(reward_config_record_id)统计
|
||||
$this->markSuccess($recordId, $resultCounts, $tierCounts);
|
||||
@@ -179,6 +146,70 @@ class WeightTestRunner
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 付费次数仅由配置决定;付费抽到「再来一次」则在队列末尾插入一条免费抽奖(同方向、同底注),可链式触发
|
||||
*/
|
||||
private function runChainFreeMode(
|
||||
int $recordId,
|
||||
PlayStartLogic $playLogic,
|
||||
int $paidS,
|
||||
int $paidN,
|
||||
int $ante,
|
||||
$paidPoolConfig,
|
||||
$freePoolConfig,
|
||||
?array $paidTierWeightsCustom,
|
||||
?array $freeTierWeightsCustom,
|
||||
$configType0,
|
||||
$configType1,
|
||||
int $safetyLine,
|
||||
bool $killEnabled,
|
||||
float &$poolProfitTotal,
|
||||
array &$resultCounts,
|
||||
array &$tierCounts,
|
||||
array &$buffer,
|
||||
int &$done
|
||||
): void {
|
||||
$queue = [];
|
||||
for ($i = 0; $i < $paidS; $i++) {
|
||||
$queue[] = ['paid', 0, $ante];
|
||||
}
|
||||
for ($i = 0; $i < $paidN; $i++) {
|
||||
$queue[] = ['paid', 1, $ante];
|
||||
}
|
||||
$qi = 0;
|
||||
while ($qi < count($queue)) {
|
||||
$item = $queue[$qi];
|
||||
$isPaid = $item[0] === 'paid';
|
||||
$dir = $item[1];
|
||||
$playAnte = $item[2];
|
||||
$lotteryType = $isPaid ? 0 : 1;
|
||||
|
||||
if ($isPaid) {
|
||||
$usePoolWeights = $killEnabled && $poolProfitTotal >= $safetyLine && $configType1 !== null;
|
||||
$cfg = $usePoolWeights ? $configType1 : $paidPoolConfig;
|
||||
$customWeights = $usePoolWeights ? null : $paidTierWeightsCustom;
|
||||
} else {
|
||||
$useKillMode = $killEnabled && $poolProfitTotal >= $safetyLine && $configType1 !== null;
|
||||
$cfg = $useKillMode ? $configType1 : $freePoolConfig;
|
||||
$customWeights = $useKillMode ? null : $freeTierWeightsCustom;
|
||||
}
|
||||
|
||||
$row = $playLogic->simulateOnePlay($cfg, $dir, $lotteryType, $playAnte, $customWeights);
|
||||
$this->accumulateProfitForDefault($row, $lotteryType, $cfg, $configType0, $poolProfitTotal);
|
||||
$this->aggregate($row, $resultCounts, $tierCounts);
|
||||
$buffer[] = $this->rowForInsert($row, $recordId);
|
||||
$done++;
|
||||
|
||||
if (!empty($row['grants_free_ticket'])) {
|
||||
$queue[] = ['free', $dir, $playAnte];
|
||||
}
|
||||
|
||||
$this->flushIfNeeded($buffer, $recordId, $done, count($queue), $resultCounts, $tierCounts, null);
|
||||
|
||||
$qi++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 累加彩金池累计盈利,用于触发杀分,与 PlayStartLogic 一致
|
||||
* @param int $lotteryType 0=付费券,1=免费券
|
||||
@@ -227,14 +258,14 @@ class WeightTestRunner
|
||||
return $out;
|
||||
}
|
||||
|
||||
private function flushIfNeeded(array &$buffer, int $recordId, int $done, int $total, array $resultCounts, array $tierCounts): void
|
||||
private function flushIfNeeded(array &$buffer, int $recordId, int $done, int $total, array $resultCounts, array $tierCounts, ?int $recordTotalPlayCount = null): void
|
||||
{
|
||||
if (count($buffer) < self::BATCH_SIZE) {
|
||||
return;
|
||||
}
|
||||
$this->insertBuffer($buffer);
|
||||
$buffer = [];
|
||||
$this->updateProgress($recordId, $done, $resultCounts, $tierCounts);
|
||||
$this->updateProgress($recordId, $done, $resultCounts, $tierCounts, $recordTotalPlayCount);
|
||||
}
|
||||
|
||||
private function insertBuffer(array $rows): void
|
||||
@@ -259,11 +290,14 @@ class WeightTestRunner
|
||||
}
|
||||
}
|
||||
|
||||
private function updateProgress(int $recordId, int $overPlayCount, array $resultCounts, array $tierCounts): void
|
||||
private function updateProgress(int $recordId, int $overPlayCount, array $resultCounts, array $tierCounts, ?int $totalPlayCount = null): void
|
||||
{
|
||||
$record = DiceRewardConfigRecord::find($recordId);
|
||||
if ($record) {
|
||||
$record->over_play_count = $overPlayCount;
|
||||
if ($totalPlayCount !== null) {
|
||||
$record->total_play_count = $totalPlayCount;
|
||||
}
|
||||
$record->result_counts = $resultCounts;
|
||||
$record->tier_counts = $tierCounts;
|
||||
$record->save();
|
||||
@@ -288,6 +322,10 @@ class WeightTestRunner
|
||||
$record->tier_counts = $tierCounts;
|
||||
$record->remark = null;
|
||||
$record->platform_profit = $platformProfit;
|
||||
$record->play_again_count = DiceRewardConfigRecord::computePlayAgainCountFromRelated($recordId);
|
||||
$actualCount = (int) DicePlayRecordTest::where('reward_config_record_id', $recordId)->count();
|
||||
$record->total_play_count = $actualCount;
|
||||
$record->test_count = $actualCount;
|
||||
$record->save();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,17 +22,16 @@ use think\model\relation\HasMany;
|
||||
* @property int|null $lottery_config_id 测试时使用的奖池配置 ID(兼容旧:付费+免费共用)
|
||||
* @property int|null $paid_lottery_config_id 付费抽奖奖池配置 ID,默认 type=0
|
||||
* @property int|null $free_lottery_config_id 免费抽奖奖池配置 ID,默认 type=1
|
||||
* @property int $total_play_count 总模拟次数(s_count+n_count)
|
||||
* @property int $total_play_count 总模拟次数
|
||||
* @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 付费抽奖顺时针次数
|
||||
* @property int $paid_n_count 付费抽奖逆时针次数
|
||||
* @property int $free_s_count 免费抽奖顺时针次数
|
||||
* @property int $free_n_count 免费抽奖逆时针次数
|
||||
* @property int $chain_free_mode 1=链式再来一次免费抽奖
|
||||
* @property int $paid_planned_spins 计划付费抽奖次数(顺+逆)
|
||||
* @property int $play_again_count 再来一次次数(T5触发次数)
|
||||
* @property array|null $paid_tier_weights 付费自定义档位权重 T1-T5
|
||||
* @property array|null $free_tier_weights 免费自定义档位权重 T1-T5
|
||||
* @property array $result_counts 落点统计 grid_number=>出现次数
|
||||
@@ -85,6 +84,18 @@ class DiceRewardConfigRecord extends BaseModel
|
||||
return round($paidAmount - $sumWinCoin, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联的 DicePlayRecordTest 统计再来一次次数(reward_tier=T5)
|
||||
* @param int $recordId
|
||||
* @return int
|
||||
*/
|
||||
public static function computePlayAgainCountFromRelated(int $recordId): int
|
||||
{
|
||||
return (int) DicePlayRecordTest::where('reward_config_record_id', $recordId)
|
||||
->where('reward_tier', 'T5')
|
||||
->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联的 DicePlayRecordTest 统计落点次数
|
||||
* result_counts = [grid_number => 出现次数],只统计 roll_number 在 5-30 之间的记录
|
||||
|
||||
Reference in New Issue
Block a user