diff --git a/app/admin/controller/game/RewardConfig.php b/app/admin/controller/game/RewardConfig.php new file mode 100644 index 0000000..4f1b163 --- /dev/null +++ b/app/admin/controller/game/RewardConfig.php @@ -0,0 +1,183 @@ +model = new \app\common\model\GameRewardConfig(); + return null; + } + + /** + * 将可访问管理员 ID 转换为可访问渠道 ID + * + * @return list + */ + protected function getDataLimitAdminIds(): array + { + if (!$this->dataLimit || !$this->auth || $this->auth->isSuperAdmin()) { + return []; + } + $adminIds = parent::getDataLimitAdminIds(); + if ($adminIds === []) { + return []; + } + $channelIds = Db::name('game_channel')->where('admin_id', 'in', $adminIds)->column('id'); + if ($channelIds === []) { + return [-1]; + } + return array_values(array_unique($channelIds)); + } + + /** + * 新增:非超管仅可写入权限内渠道 + * @throws Throwable + */ + protected function _add(): Response + { + if ($this->request && $this->request->method() === 'POST' && !$this->auth->isSuperAdmin()) { + $allowedChannelIds = $this->getDataLimitAdminIds(); + $cid = $this->request->post('game_channel_id'); + if ($cid === null || $cid === '' || ($allowedChannelIds !== [] && !in_array($cid, $allowedChannelIds))) { + return $this->error(__('You have no permission')); + } + } + return parent::_add(); + } + + /** + * 编辑:非超管锁定渠道,不允许跨渠道改写 + * @throws Throwable + */ + protected function _edit(): Response + { + $pk = $this->model->getPk(); + $id = $this->request ? ($this->request->post($pk) ?? $this->request->get($pk)) : null; + $row = $this->model->find($id); + if (!$row) { + return $this->error(__('Record not found')); + } + + $dataLimitAdminIds = $this->getDataLimitAdminIds(); + if ($dataLimitAdminIds && !in_array($row[$this->dataLimitField], $dataLimitAdminIds)) { + return $this->error(__('You have no permission')); + } + + if ($this->request && $this->request->method() === 'POST') { + $data = $this->request->post(); + if (!$data) { + return $this->error(__('Parameter %s can not be empty', [''])); + } + + $data = $this->applyInputFilter($data); + $data = $this->excludeFields($data); + + if (!$this->auth->isSuperAdmin()) { + $data[$this->dataLimitField] = $row[$this->dataLimitField]; + } + + $result = false; + $this->model->startTrans(); + try { + if ($this->modelValidate) { + $validate = str_replace("\\model\\", "\\validate\\", get_class($this->model)); + if (class_exists($validate)) { + $validate = new $validate(); + if ($this->modelSceneValidate) { + $validate->scene('edit'); + } + $data[$pk] = $row[$pk]; + $validate->check($data); + } + } + $result = $row->save($data); + $this->model->commit(); + } catch (Throwable $e) { + $this->model->rollback(); + return $this->error($e->getMessage()); + } + if ($result !== false) { + return $this->success(__('Update successful')); + } + return $this->error(__('No rows updated')); + } + + return $this->success('', ['row' => $row]); + } + + /** + * 查看 + * @throws Throwable + */ + protected function _index(): Response + { + // 如果是 select 则转发到 select 方法,若未重写该方法,其实还是继续执行 index + if ($this->request && $this->request->get('select')) { + return $this->select($this->request); + } + + /** + * 1. withJoin 不可使用 alias 方法设置表别名,别名将自动使用关联模型名称(小写下划线命名规则) + * 2. 以下的别名设置了主表别名,同时便于拼接查询参数等 + * 3. paginate 数据集可使用链式操作 each(function($item, $key) {}) 遍历处理 + */ + list($where, $alias, $limit, $order) = $this->queryBuilder(); + $res = $this->model + ->withJoin($this->withJoinTable, $this->withJoinType) + ->with($this->withJoinTable) + ->visible(['gameChannel' => ['name']]) + ->alias($alias) + ->where($where) + ->order($order) + ->paginate($limit); + + return $this->success('', [ + 'list' => $res->items(), + 'total' => $res->total(), + 'remark' => get_route_remark(), + ]); + } + + /** + * 若需重写查看、编辑、删除等方法,请复制 @see \app\admin\library\traits\Backend 中对应的方法至此进行重写 + */ +} \ No newline at end of file diff --git a/app/common/model/GameRewardConfig.php b/app/common/model/GameRewardConfig.php new file mode 100644 index 0000000..7cb75d5 --- /dev/null +++ b/app/common/model/GameRewardConfig.php @@ -0,0 +1,29 @@ + 'integer', + 'update_time' => 'integer', + ]; + + + public function gameChannel(): \think\model\relation\BelongsTo + { + return $this->belongsTo(\app\common\model\GameChannel::class, 'game_channel_id', 'id'); + } +} \ No newline at end of file diff --git a/app/common/validate/GameRewardConfig.php b/app/common/validate/GameRewardConfig.php new file mode 100644 index 0000000..aa16132 --- /dev/null +++ b/app/common/validate/GameRewardConfig.php @@ -0,0 +1,143 @@ + 'require|integer|gt:0', + 'tier_reward_form' => 'require|checkTierRewardForm', + 'bigwin_form' => 'require|checkBigwinForm', + ]; + + /** + * 提示消息 + */ + protected $message = [ + 'game_channel_id.require' => '请选择渠道', + 'game_channel_id.integer' => '渠道参数格式错误', + 'game_channel_id.gt' => '渠道参数格式错误', + 'tier_reward_form.require' => '档位奖励表单不能为空', + 'bigwin_form.require' => '超级大奖表单不能为空', + ]; + + /** + * 验证场景 + */ + protected $scene = [ + 'add' => ['game_channel_id', 'tier_reward_form', 'bigwin_form'], + 'edit' => ['game_channel_id', 'tier_reward_form', 'bigwin_form'], + ]; + + private function parseJsonArray(mixed $value, string $label): array|string + { + if (!is_string($value) || trim($value) === '') { + return $label . '不能为空'; + } + + try { + $decoded = json_decode($value, true, 512, JSON_THROW_ON_ERROR); + } catch (\Throwable $e) { + return $label . '必须为合法 JSON'; + } + + if (!is_array($decoded)) { + return $label . '格式错误'; + } + return $decoded; + } + + protected function checkTierRewardForm($value, $rule, array $data = []): bool|string + { + $decoded = $this->parseJsonArray($value, '档位奖励表单'); + if (!is_array($decoded)) { + return $decoded; + } + + $expectedGrid = range(5, 30); + if (count($decoded) !== 26) { + return '档位奖励表单必须固定 26 条'; + } + $allowTiers = ['T1', 'T2', 'T3', 'T4', 'T5']; + foreach ($decoded as $idx => $row) { + $rowNo = $idx + 1; + if (!is_array($row)) { + return '档位奖励表单第' . $rowNo . '条格式错误'; + } + $gridNumber = $row['grid_number'] ?? null; + $uiText = $row['ui_text'] ?? null; + $realEv = $row['real_ev'] ?? null; + $tier = $row['tier'] ?? null; + + if (!is_numeric($gridNumber)) { + return '档位奖励表单第' . $rowNo . '条点数必须为数字'; + } + $gridInt = intval(strval($gridNumber)); + if ($gridInt !== $expectedGrid[$idx]) { + return '档位奖励表单点数必须固定为 5-30 且不可修改'; + } + if (!is_string($uiText) || trim($uiText) === '') { + return '档位奖励表单第' . $rowNo . '条显示文本不能为空'; + } + if ($realEv === null || $realEv === '' || !is_numeric($realEv)) { + return '档位奖励表单第' . $rowNo . '条实际中奖必须为数字'; + } + if (!is_string($tier) || !in_array($tier, $allowTiers, true)) { + return '档位奖励表单第' . $rowNo . '条档位只能是 T1-T5'; + } + } + + return true; + } + + protected function checkBigwinForm($value, $rule, array $data = []): bool|string + { + $decoded = $this->parseJsonArray($value, '超级大奖表单'); + if (!is_array($decoded)) { + return $decoded; + } + + $expectedGrid = [5, 10, 15, 20, 25, 30]; + if (count($decoded) !== 6) { + return '超级大奖表单必须固定 6 条'; + } + foreach ($decoded as $idx => $row) { + $rowNo = $idx + 1; + if (!is_array($row)) { + return '超级大奖表单第' . $rowNo . '条格式错误'; + } + + $gridNumber = $row['grid_number'] ?? null; + $uiText = $row['ui_text'] ?? null; + $realEv = $row['real_ev'] ?? null; + $tier = $row['tier'] ?? null; + + if (!is_numeric($gridNumber)) { + return '超级大奖表单第' . $rowNo . '条点数必须为数字'; + } + $gridInt = intval(strval($gridNumber)); + if ($gridInt !== $expectedGrid[$idx]) { + return '超级大奖表单点数必须固定为 5、10、15、20、25、30 且不可修改'; + } + + if (!is_string($uiText) || trim($uiText) === '') { + return '超级大奖表单第' . $rowNo . '条显示文本不能为空'; + } + if ($realEv === null || $realEv === '' || !is_numeric($realEv)) { + return '超级大奖表单第' . $rowNo . '条实际中奖必须为数字'; + } + if (!is_string($tier) || $tier !== 'BIGWIN') { + return '超级大奖表单第' . $rowNo . '条档位必须为 BIGWIN'; + } + } + return true; + } + +} diff --git a/web/src/lang/backend/en/game/rewardConfig.ts b/web/src/lang/backend/en/game/rewardConfig.ts new file mode 100644 index 0000000..168d66d --- /dev/null +++ b/web/src/lang/backend/en/game/rewardConfig.ts @@ -0,0 +1,22 @@ +export default { + id: 'id', + game_channel_id: 'game_channel_id', + gamechannel__name: 'name', + tier_reward_form: 'tier_reward_form', + bigwin_form: 'bigwin_form', + create_time: 'create_time', + update_time: 'update_time', + grid_number: 'grid_number', + ui_text: 'ui_text', + real_ev: 'real_ev', + tier: 'tier', + tier_t1: 'T1', + tier_t2: 'T2', + tier_t3: 'T3', + tier_t4: 'T4', + tier_t5: 'T5', + tier_bigwin: 'BIGWIN', + tier_reward_form_help: 'Fixed 26 rows (5-30), no add/delete. Editable: ui_text, real_ev, tier.', + bigwin_form_help: 'Fixed 6 rows (5,10,15,20,25,30), no add/delete. Editable: ui_text, real_ev.', + 'quick Search Fields': 'id', +} diff --git a/web/src/lang/backend/zh-cn/game/rewardConfig.ts b/web/src/lang/backend/zh-cn/game/rewardConfig.ts new file mode 100644 index 0000000..39593ce --- /dev/null +++ b/web/src/lang/backend/zh-cn/game/rewardConfig.ts @@ -0,0 +1,22 @@ +export default { + id: 'ID', + game_channel_id: '渠道', + gamechannel__name: '渠道名', + tier_reward_form: '档位奖励表单', + bigwin_form: '超级大奖表单', + create_time: '创建时间', + update_time: '更新时间', + grid_number: '色子点数', + ui_text: '显示文本', + real_ev: '实际中奖', + tier: '档位', + tier_t1: 'T1', + tier_t2: 'T2', + tier_t3: 'T3', + tier_t4: 'T4', + tier_t5: 'T5', + tier_bigwin: 'BIGWIN', + tier_reward_form_help: '固定 26 条(点数 5-30),不可新增或删除,仅可修改显示文本、实际中奖、档位', + bigwin_form_help: '固定 6 条(点数 5、10、15、20、25、30),不可新增或删除,仅可修改显示文本、实际中奖', + 'quick Search Fields': 'ID', +} diff --git a/web/src/views/backend/game/rewardConfig/BigwinFormCell.vue b/web/src/views/backend/game/rewardConfig/BigwinFormCell.vue new file mode 100644 index 0000000..02f9bae --- /dev/null +++ b/web/src/views/backend/game/rewardConfig/BigwinFormCell.vue @@ -0,0 +1,59 @@ + + + + + diff --git a/web/src/views/backend/game/rewardConfig/TierRewardFormCell.vue b/web/src/views/backend/game/rewardConfig/TierRewardFormCell.vue new file mode 100644 index 0000000..55b2acb --- /dev/null +++ b/web/src/views/backend/game/rewardConfig/TierRewardFormCell.vue @@ -0,0 +1,59 @@ + + + + + diff --git a/web/src/views/backend/game/rewardConfig/index.vue b/web/src/views/backend/game/rewardConfig/index.vue new file mode 100644 index 0000000..a2c4135 --- /dev/null +++ b/web/src/views/backend/game/rewardConfig/index.vue @@ -0,0 +1,141 @@ + + + + + diff --git a/web/src/views/backend/game/rewardConfig/popupForm.vue b/web/src/views/backend/game/rewardConfig/popupForm.vue new file mode 100644 index 0000000..1fd8fd5 --- /dev/null +++ b/web/src/views/backend/game/rewardConfig/popupForm.vue @@ -0,0 +1,264 @@ + + + + +