feat: 增强管理员权限与角色管理功能

- 在 SyncAdminAuthorizationCommand 中新增对代理和抽奖菜单操作的同步功能,确保缺失的菜单操作行能够被创建。
- 更新多个控制器中的权限检查逻辑,使用 hasPermissionCode 替代原有的权限验证方式,提升权限管理的灵活性。
- 引入 ApiMessage 统一错误响应格式,确保在权限不足时返回一致的错误信息。
- 更新 AdminRole 和 AdminUser 模型,增强角色与用户的权限管理功能,支持更细粒度的权限控制。
This commit is contained in:
2026-06-03 10:56:36 +08:00
parent 1dcd4716c5
commit 0527c7c392
96 changed files with 2215 additions and 139 deletions

View File

@@ -23,6 +23,8 @@ return [
'agent_node_has_children_cannot_delete' => 'This agent node has child nodes. Delete children first.',
'agent_node_has_users_cannot_delete' => 'This agent node still has bound admin accounts and cannot be deleted.',
'agent_node_has_roles_cannot_delete' => 'This agent node still has bound roles and cannot be deleted.',
'agent_role_in_use' => 'This role is still assigned to :count account(s). Unbind them in Accounts before deleting.',
'agent_role_read_only' => 'Read-only template roles cannot be changed or deleted.',
'user_cannot_delete_self' => 'Cannot delete your own account.',
'user_cannot_delete_last_super_admin' => 'Cannot delete the last super admin.',
'super_admin_only_for_roles' => 'Only super admins can manage roles.',

11
lang/en/validation.php Normal file
View File

@@ -0,0 +1,11 @@
<?php
return array_merge(
require __DIR__.'/validation_rules.php',
[
'attributes' => require __DIR__.'/validation_attributes.php',
'custom' => require __DIR__.'/validation_custom.php',
'business' => require __DIR__.'/validation_business.php',
'exact' => require __DIR__.'/validation_exact.php',
],
);

View File

@@ -0,0 +1,62 @@
<?php
return [
'account' => 'account',
'password' => 'password',
'captcha_key' => 'captcha',
'captcha_code' => 'captcha code',
'username' => 'username',
'nickname' => 'nickname',
'name' => 'name',
'code' => 'code',
'slug' => 'slug',
'status' => 'status',
'email' => 'email',
'group' => 'settings group',
'value' => 'value',
'key' => 'key',
'items' => 'items',
'parent_id' => 'parent node',
'role_ids' => 'roles',
'role_slugs' => 'roles',
'permission_slugs' => 'permissions',
'grants' => 'delegation grants',
'child_agent_id' => 'child agent',
'role' => 'role',
'user' => 'user',
'site_code' => 'site code',
'site_player_id' => 'site player ID',
'player_id' => 'player',
'agent_node_id' => 'agent node',
'currency_code' => 'currency',
'currency' => 'currency',
'amount' => 'amount',
'amount_delta' => 'adjustment amount',
'reason' => 'reason',
'remark' => 'remark',
'draw_id' => 'draw',
'draw_no' => 'draw number',
'draw_time' => 'draw time',
'start_time' => 'start time',
'close_time' => 'close time',
'period' => 'period',
'date_from' => 'start date',
'date_to' => 'end date',
'play_code' => 'play',
'page' => 'page',
'per_page' => 'per page',
'lines' => 'bet lines',
'lines.*.number' => 'number',
'lines.*.play_code' => 'play',
'lines.*.amount' => 'stake',
'idempotent_key' => 'idempotency key',
'wallet_api_url' => 'wallet API URL',
'prize_type' => 'prize type',
'prize_index' => 'prize index',
'number_4d' => '4-digit number',
'normalized_number' => 'number',
'items.*.play_code' => 'play',
'items.*.odds_value' => 'odds',
'items.*.display_name' => 'display name',
'report_type' => 'report type',
];

View File

@@ -0,0 +1,16 @@
<?php
return [
'unique' => 'This value already exists. Please choose another.',
'required' => ':attribute is required.',
'system_role' => 'System roles cannot be deleted.',
'agent_mismatch' => 'This user does not belong to the current agent node.',
'invalid_for_agent' => 'One or more roles are not valid for this agent.',
'not_manageable' => 'You cannot manage this child agent.',
'invalid_menu_action' => 'Invalid or unknown permission item.',
'exceeds_actor' => 'These permissions exceed what you may grant: :detail',
'exceeds_parent_ceiling' => 'These permissions exceed the parent delegation ceiling: :detail',
'exceeds_delegation_ceiling' => 'These permissions exceed this node\'s delegation ceiling: :detail',
'permission_exceeds_actor' => 'These permissions exceed what you may grant: :detail',
'permission_catalog_incomplete' => 'Permission catalog is incomplete (missing: :detail). Run migrate and admin-auth-sync.',
];

View File

@@ -0,0 +1,44 @@
<?php
return [
'code' => [
'regex' => 'Code may only contain letters, digits, underscores, and hyphens; site codes must start with a lowercase letter or digit.',
'unique' => 'This code is already in use.',
],
'slug' => [
'regex' => 'Slug may only contain lowercase letters, digits, underscores, and hyphens.',
'unique' => 'This slug is already in use.',
],
'account' => [
'regex' => 'Account may only contain letters, digits, dots, underscores, and hyphens.',
],
'username' => [
'regex' => 'Username may only contain letters, digits, dots, underscores, and hyphens.',
'unique' => 'This username is already in use.',
],
'draw_no' => [
'regex' => 'Draw number must match YYYYMMDD-sequence (e.g. 20260101-001).',
],
'number_4d' => [
'regex' => 'Number must be exactly 4 digits.',
],
'normalized_number' => [
'regex' => 'Number must be exactly 4 digits.',
'size' => 'Number must be exactly 4 digits.',
],
'wallet_api_url' => [
'wallet_api_url' => 'Wallet API URL must be an https public root URL (no localhost, private IP, path, or query).',
],
'amount_delta' => [
'not_in' => 'Adjustment amount cannot be zero.',
],
'password' => [
'min' => 'Password must be at least :min characters.',
],
'reason' => [
'min' => 'Reason must be at least :min characters.',
],
'items' => [
'size' => ':attribute must contain exactly :size items.',
],
];

View File

@@ -0,0 +1,6 @@
<?php
return [
'items must contain the complete 23 draw prize slots.' => 'All 23 draw prize slots must be provided (first, second, third, starter, and consolation).',
'items_must_contain_23_slots' => 'All 23 draw prize slots must be provided (first, second, third, starter, and consolation).',
];

View File

@@ -0,0 +1,54 @@
<?php
return [
'accepted' => 'You must accept :attribute.',
'array' => ':attribute must be a list.',
'boolean' => ':attribute must be true or false.',
'confirmed' => ':attribute confirmation does not match.',
'date' => ':attribute must be a valid date.',
'date_format' => ':attribute must match the format :format.',
'email' => ':attribute must be a valid email address.',
'exists' => 'The selected :attribute is invalid or unavailable.',
'filled' => ':attribute must have a value.',
'in' => 'The selected :attribute is invalid.',
'integer' => ':attribute must be an integer.',
'json' => ':attribute must be valid JSON.',
'max' => [
'array' => ':attribute may not have more than :max items.',
'numeric' => ':attribute may not be greater than :max.',
'string' => ':attribute may not exceed :max characters.',
],
'min' => [
'array' => ':attribute must have at least :min items.',
'numeric' => ':attribute must be at least :min.',
'string' => ':attribute must be at least :min characters.',
],
'not_in' => 'The selected :attribute is invalid.',
'not_regex' => ':attribute format is invalid.',
'numeric' => ':attribute must be a number.',
'present' => ':attribute must be present.',
'prohibited' => ':attribute is not allowed.',
'regex' => ':attribute format is invalid.',
'required' => ':attribute is required.',
'required_if' => ':attribute is required when :other is :value.',
'required_with' => ':attribute is required when :values is present.',
'required_with_all' => ':attribute is required when :values are present.',
'required_without' => ':attribute is required when :values is not present.',
'same' => ':attribute must match :other.',
'size' => [
'array' => ':attribute must contain :size items.',
'numeric' => ':attribute must be :size.',
'string' => ':attribute must be :size characters.',
],
'string' => ':attribute must be text.',
'unique' => ':attribute is already taken.',
'url' => ':attribute must be a valid URL.',
'uuid' => ':attribute must be a valid UUID.',
'between' => [
'array' => ':attribute must have between :min and :max items.',
'numeric' => ':attribute must be between :min and :max.',
'string' => ':attribute must be between :min and :max characters.',
],
'after_or_equal' => ':attribute must be on or after :date.',
'distinct' => ':attribute has a duplicate value.',
];