diff --git a/saiadmin-artd/src/views/plugin/dice/api/reward_config/index.ts b/saiadmin-artd/src/views/plugin/dice/api/reward_config/index.ts index 86d79ab..2df7fda 100644 --- a/saiadmin-artd/src/views/plugin/dice/api/reward_config/index.ts +++ b/saiadmin-artd/src/views/plugin/dice/api/reward_config/index.ts @@ -63,6 +63,16 @@ export default { }) }, + /** + * 批量更新奖励索引配置(第一页:id、grid_number、ui_text、real_ev、tier、remark) + */ + batchUpdate(items: Array<{ id: number; grid_number?: number; ui_text?: string; real_ev?: number; tier?: string; remark?: string }>) { + return request.post({ + url: '/core/dice/reward_config/DiceRewardConfig/batchUpdate', + data: { items } + }) + }, + /** * T1-T5、BIGWIN 权重配比:按档位分组获取配置列表 */ @@ -83,6 +93,16 @@ export default { }) }, + /** + * 大奖权重:按 grid_number 批量保存 BIGWIN 权重(无需 reward id,不存在则自动创建) + */ + saveBigwinWeightsByGrid(items: Array<{ grid_number: number; weight: number }>) { + return request.post({ + url: '/core/dice/reward_config/DiceRewardConfig/saveBigwinWeightsByGrid', + data: { items } + }) + }, + /** * 创建奖励对照:按当前奖励配置为顺时针(0)、逆时针(1)生成所有色子可能对应的 dice_reward 记录,权重默认 1,可在奖励对照页权重编辑中调整 */ diff --git a/saiadmin-artd/src/views/plugin/dice/reward_config/index/index.vue b/saiadmin-artd/src/views/plugin/dice/reward_config/index/index.vue index 1a4d182..b6b9296 100644 --- a/saiadmin-artd/src/views/plugin/dice/reward_config/index/index.vue +++ b/saiadmin-artd/src/views/plugin/dice/reward_config/index/index.vue @@ -1,78 +1,228 @@ + + diff --git a/server/app/dice/controller/reward_config/DiceRewardConfigController.php b/server/app/dice/controller/reward_config/DiceRewardConfigController.php index c5b8d2b..3172fbf 100644 --- a/server/app/dice/controller/reward_config/DiceRewardConfigController.php +++ b/server/app/dice/controller/reward_config/DiceRewardConfigController.php @@ -104,6 +104,29 @@ class DiceRewardConfigController extends BaseController } } + /** + * 批量更新奖励索引配置(第一页:id、grid_number、ui_text、real_ev、tier、remark) + * @param Request $request items: [{ id, grid_number?, ui_text?, real_ev?, tier?, remark? }, ...] + * @return Response + */ + #[Permission('奖励配置修改', 'dice:reward_config:index:update')] + public function batchUpdate(Request $request): Response + { + $items = $request->post('items', []); + if (! is_array($items)) { + return $this->fail('参数 items 必须为数组'); + } + $err = $this->logic->validateBatchUpdateItems($items); + if ($err !== null) { + return $this->fail($err); + } + foreach ($items as $item) { + $this->validate('batch_update', array_merge($item, ['id' => $item['id']])); + } + $this->logic->batchUpdate($items); + return $this->success('保存成功'); + } + /** * 删除数据 * @param Request $request @@ -159,6 +182,27 @@ class DiceRewardConfigController extends BaseController } } + /** + * 大奖权重:按 grid_number 批量保存 BIGWIN 权重(仅更新 dice_reward_config 表,不操作 dice_reward) + * items: [ { grid_number: 5-30, weight: 0-10000 }, ... ] + * @param Request $request + * @return Response + */ + #[Permission('奖励配置修改', 'dice:reward_config:index:update')] + public function saveBigwinWeightsByGrid(Request $request): Response + { + $items = $request->post('items', []); + if (! is_array($items)) { + return $this->fail('参数 items 必须为数组'); + } + $err = $this->logic->validateBigwinWeightItems($items); + if ($err !== null) { + return $this->fail($err); + } + $this->logic->batchUpdateBigwinWeight($items); + return $this->success('保存成功'); + } + /** * 创建奖励对照:按当前 dice_reward_config 为两种方向(顺时针0、逆时针1)生成所有色子可能对应的 dice_reward 记录 * 权重默认 1,可在「奖励对照」页的权重编辑弹窗中调整 diff --git a/server/app/dice/logic/reward/DiceRewardLogic.php b/server/app/dice/logic/reward/DiceRewardLogic.php index e6202dd..43bcbaf 100644 --- a/server/app/dice/logic/reward/DiceRewardLogic.php +++ b/server/app/dice/logic/reward/DiceRewardLogic.php @@ -226,37 +226,46 @@ class DiceRewardLogic /** * 更新 BIGWIN 档位某点数的权重(顺/逆时针同时更新);0=0% 中奖,10000=100% 中奖 + * 表 dice_reward 唯一键为 (direction, grid_number),同一点数同一方向仅一条记录,故先按该键查找再更新,避免重复插入 */ public function updateBigwinWeight(int $gridNumber, int $weight): void { $weight = min(self::BIGWIN_WEIGHT_MAX, max(0, $weight)); + $config = DiceRewardConfig::where('tier', 'BIGWIN') + ->where('grid_number', $gridNumber) + ->find(); + if (! $config) { + return; + } + $configArr = $config->toArray(); foreach ([DiceReward::DIRECTION_CLOCKWISE, DiceReward::DIRECTION_COUNTERCLOCKWISE] as $direction) { - // 优先更新已存在记录 - $affected = DiceReward::where('tier', 'BIGWIN') - ->where('direction', $direction) + // 按唯一键 (direction, grid_number) 查找,存在则更新,不存在则插入 + $row = DiceReward::where('direction', $direction) ->where('grid_number', $gridNumber) - ->update(['weight' => $weight]); - - // 若不存在 BIGWIN 记录,则按当前 BIGWIN 配置懒加载创建一条 dice_reward 记录 - if ($affected === 0) { - $config = DiceRewardConfig::where('tier', 'BIGWIN') - ->where('grid_number', $gridNumber) - ->find(); - if ($config) { - $m = new DiceReward(); - $m->tier = 'BIGWIN'; - $m->direction = $direction; - $m->grid_number = (int) $gridNumber; - // 对于 BIGWIN,仅需保证 real_ev、weight、grid_number,start_index/end_index 取当前配置 id 即可 - $m->start_index = (int) $config->id; - $m->end_index = (int) $config->id; - $m->ui_text = (string) ($config->ui_text ?? ''); - $m->real_ev = (float) ($config->real_ev ?? 0); - $m->remark = (string) ($config->remark ?? ''); - $m->type = $config->type ?? null; - $m->weight = $weight > 0 ? $weight : self::WEIGHT_MIN; - $m->save(); - } + ->find(); + if ($row) { + $row->tier = 'BIGWIN'; + $row->weight = $weight > 0 ? $weight : self::WEIGHT_MIN; + $row->start_index = (int) ($configArr['id'] ?? $row->start_index); + $row->end_index = (int) ($configArr['id'] ?? $row->end_index); + $row->ui_text = (string) ($configArr['ui_text'] ?? $row->ui_text); + $row->real_ev = (float) ($configArr['real_ev'] ?? $row->real_ev); + $row->remark = (string) ($configArr['remark'] ?? $row->remark); + $row->type = $configArr['type'] ?? $row->type; + $row->save(); + } else { + $m = new DiceReward(); + $m->tier = 'BIGWIN'; + $m->direction = $direction; + $m->grid_number = (int) $gridNumber; + $m->start_index = (int) ($configArr['id'] ?? 0); + $m->end_index = (int) ($configArr['id'] ?? 0); + $m->ui_text = (string) ($configArr['ui_text'] ?? ''); + $m->real_ev = (float) ($configArr['real_ev'] ?? 0); + $m->remark = (string) ($configArr['remark'] ?? ''); + $m->type = $configArr['type'] ?? null; + $m->weight = $weight > 0 ? $weight : self::WEIGHT_MIN; + $m->save(); } } DiceReward::refreshCache(); diff --git a/server/app/dice/logic/reward_config/DiceRewardConfigLogic.php b/server/app/dice/logic/reward_config/DiceRewardConfigLogic.php index 868752f..360ccdf 100644 --- a/server/app/dice/logic/reward_config/DiceRewardConfigLogic.php +++ b/server/app/dice/logic/reward_config/DiceRewardConfigLogic.php @@ -68,6 +68,158 @@ class DiceRewardConfigLogic extends BaseLogic return $listResult; } + /** 奖励索引必须为 26 条,id 为 0~25,点数 5~30 各出现一次 */ + private const BATCH_INDEX_COUNT = 26; + private const INDEX_ID_MIN = 0; + private const INDEX_ID_MAX = 25; + private const GRID_NUMBER_MIN = 5; + private const GRID_NUMBER_MAX = 30; + + /** + * 校验批量更新项(奖励索引表单独立提交,可能只含非 BIGWIN 的若干条) + * - 每项必须包含 id、grid_number;grid_number 须在 5~30,提交项内 grid_number 不能重复 + * - 若为 26 条则额外校验:id 为 0~25 各一、grid_number 为 5~30 各一 + * @return string|null 校验失败返回错误信息,通过返回 null + */ + public function validateBatchUpdateItems(array $items): ?string + { + if (count($items) === 0) { + return '提交数据不能为空'; + } + $ids = []; + $gridNumbers = []; + foreach ($items as $item) { + if (! array_key_exists('id', $item) || $item['id'] === null || $item['id'] === '') { + return '每项必须包含 id'; + } + $id = (int) $item['id']; + $ids[] = $id; + if (! array_key_exists('grid_number', $item)) { + return '每项必须包含 grid_number'; + } + $gn = (int) $item['grid_number']; + if ($gn < self::GRID_NUMBER_MIN || $gn > self::GRID_NUMBER_MAX) { + return '色子点数 grid_number 只能为 ' . self::GRID_NUMBER_MIN . '~' . self::GRID_NUMBER_MAX . ',当前存在 ' . $gn; + } + $gridNumbers[] = $gn; + } + $gridDuplicates = $this->findDuplicateValues($gridNumbers); + if ($gridDuplicates !== []) { + sort($gridDuplicates); + return '色子点数在本批内不能重复,重复的点数为:' . implode('、', $gridDuplicates); + } + $cnt = count($items); + if ($cnt === self::BATCH_INDEX_COUNT) { + foreach ($ids as $id) { + if ($id < self::INDEX_ID_MIN || $id > self::INDEX_ID_MAX) { + return '索引 id 只能为 ' . self::INDEX_ID_MIN . '~' . self::INDEX_ID_MAX . ',当前存在 id=' . $id; + } + } + $idDuplicates = $this->findDuplicateValues($ids); + if ($idDuplicates !== []) { + sort($idDuplicates); + return '索引 id 必须为 0~25 各出现一次不能重复,重复的 id 为:' . implode('、', $idDuplicates); + } + $requiredIds = range(self::INDEX_ID_MIN, self::INDEX_ID_MAX); + if (array_diff($requiredIds, $ids) !== [] || array_diff($ids, $requiredIds) !== []) { + return '索引 id 必须且只能为 0~25 各一个'; + } + $requiredGrid = range(self::GRID_NUMBER_MIN, self::GRID_NUMBER_MAX); + if (array_diff($requiredGrid, $gridNumbers) !== [] || array_diff($gridNumbers, $requiredGrid) !== []) { + return '色子点数必须且只能为 5~30 各一个'; + } + } + return null; + } + + /** + * 找出数组中出现多于一次的值 + * @param array $arr + * @return array 重复出现的值(去重) + */ + private function findDuplicateValues(array $arr): array + { + $counts = array_count_values($arr); + $duplicates = []; + foreach ($counts as $value => $count) { + if ($count > 1) { + $duplicates[] = $value; + } + } + return $duplicates; + } + + /** + * 批量更新奖励索引配置:grid_number、ui_text、real_ev、tier、remark(不含 weight,BIGWIN 权重单独接口) + * @param array $items 每项 [id, grid_number?, ui_text?, real_ev?, tier?, remark?] + */ + public function batchUpdate(array $items): void + { + foreach ($items as $row) { + if (! array_key_exists('id', $row) || $row['id'] === null || $row['id'] === '') { + continue; + } + $id = (int) $row['id']; + $data = []; + foreach (['grid_number', 'ui_text', 'real_ev', 'tier', 'remark'] as $field) { + if (array_key_exists($field, $row)) { + $data[$field] = $row[$field]; + } + } + if (! empty($data)) { + parent::edit($id, $data); + } + } + DiceRewardConfig::refreshCache(); + } + + /** + * 校验大奖权重提交项:点数 5~30,本批内 grid_number 不能重复 + * @return string|null 校验失败返回错误信息(含重复的点数),通过返回 null + */ + public function validateBigwinWeightItems(array $items): ?string + { + if (count($items) === 0) { + return '提交数据不能为空'; + } + $gridNumbers = []; + foreach ($items as $row) { + $gn = isset($row['grid_number']) ? (int) $row['grid_number'] : 0; + if ($gn < self::GRID_NUMBER_MIN || $gn > self::GRID_NUMBER_MAX) { + return '色子点数 grid_number 只能为 ' . self::GRID_NUMBER_MIN . '~' . self::GRID_NUMBER_MAX . ',当前存在 ' . $gn; + } + $gridNumbers[] = $gn; + } + $duplicates = $this->findDuplicateValues($gridNumbers); + if ($duplicates !== []) { + sort($duplicates); + return '大奖权重本批内点数不能重复,重复的点数为:' . implode('、', $duplicates); + } + return null; + } + + /** + * 批量更新 BIGWIN 档位权重(仅写 dice_reward_config 表,不操作 dice_reward) + * @param array $items 每项 [grid_number => 5-30, weight => 0-10000] + */ + public function batchUpdateBigwinWeight(array $items): void + { + $weightMin = 0; + $weightMax = 10000; + foreach ($items as $row) { + $gridNumber = isset($row['grid_number']) ? (int) $row['grid_number'] : 0; + $weight = isset($row['weight']) ? (int) $row['weight'] : 0; + if ($gridNumber < 5 || $gridNumber > 30) { + continue; + } + $weight = max($weightMin, min($weightMax, $weight)); + $this->model->where('tier', 'BIGWIN') + ->where('grid_number', $gridNumber) + ->update(['weight' => $weight]); + } + DiceRewardConfig::refreshCache(); + } + /** * 删除后刷新缓存 */ diff --git a/server/app/dice/validate/reward_config/DiceRewardConfigValidate.php b/server/app/dice/validate/reward_config/DiceRewardConfigValidate.php index 9300b2f..a9b5bd4 100644 --- a/server/app/dice/validate/reward_config/DiceRewardConfigValidate.php +++ b/server/app/dice/validate/reward_config/DiceRewardConfigValidate.php @@ -13,25 +13,31 @@ use plugin\saiadmin\basic\BaseValidate; */ class DiceRewardConfigValidate extends BaseValidate { + /** 色子点数范围:5~30 共 26 个点数 */ + public const GRID_NUMBER_MIN = 5; + public const GRID_NUMBER_MAX = 30; + protected $rule = [ - 'grid_number' => 'require', + 'grid_number' => 'require|integer|between:5,30', 'ui_text' => 'require', 'real_ev' => 'require', 'tier' => 'require', - 'type' => 'number', + 'type' => 'number', 'weight' => 'number|between:0,10000', // BIGWIN 大奖权重,仅档位为 BIGWIN 时使用 + 'remark' => 'max:500', ]; protected $message = [ - 'grid_number' => '色子点数必须填写', + 'grid_number' => '色子点数必须为 5~30 之间的整数(共26个点数)', 'ui_text' => '前端显示文本必须填写', 'real_ev' => '真实资金结算必须填写', 'tier' => '所属档位必须填写', - 'type' => '奖励类型须为数字', + 'type' => '奖励类型须为数字', ]; protected $scene = [ - 'save' => ['grid_number', 'ui_text', 'real_ev', 'tier', 'type'], - 'update' => ['grid_number', 'ui_text', 'real_ev', 'tier', 'type', 'weight'], + 'save' => ['grid_number', 'ui_text', 'real_ev', 'tier', 'type'], + 'update' => ['grid_number', 'ui_text', 'real_ev', 'tier', 'type', 'weight'], + 'batch_update' => ['grid_number', 'ui_text', 'real_ev', 'tier', 'remark'], ]; } diff --git a/server/plugin/saiadmin/config/route.php b/server/plugin/saiadmin/config/route.php index c0ae026..5003868 100644 --- a/server/plugin/saiadmin/config/route.php +++ b/server/plugin/saiadmin/config/route.php @@ -112,6 +112,8 @@ Route::group('/core', function () { fastRoute('dice/reward_config/DiceRewardConfig', \app\dice\controller\reward_config\DiceRewardConfigController::class); Route::get('/dice/reward_config/DiceRewardConfig/weightRatioList', [\app\dice\controller\reward_config\DiceRewardConfigController::class, 'weightRatioList']); Route::post('/dice/reward_config/DiceRewardConfig/batchUpdateWeights', [\app\dice\controller\reward_config\DiceRewardConfigController::class, 'batchUpdateWeights']); + Route::post('/dice/reward_config/DiceRewardConfig/saveBigwinWeightsByGrid', [\app\dice\controller\reward_config\DiceRewardConfigController::class, 'saveBigwinWeightsByGrid']); + Route::post('/dice/reward_config/DiceRewardConfig/batchUpdate', [\app\dice\controller\reward_config\DiceRewardConfigController::class, 'batchUpdate']); Route::post('/dice/reward_config/DiceRewardConfig/createRewardReference', [\app\dice\controller\reward_config\DiceRewardConfigController::class, 'createRewardReference']); Route::post('/dice/reward_config/DiceRewardConfig/runWeightTest', [\app\dice\controller\reward_config\DiceRewardConfigController::class, 'runWeightTest']); fastRoute('dice/lottery_pool_config/DiceLotteryPoolConfig', \app\dice\controller\lottery_pool_config\DiceLotteryPoolConfigController::class);