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.',
];

View File

@@ -23,6 +23,8 @@ return [
'agent_node_has_children_cannot_delete' => 'यस एजेन्ट नोडमा चाइल्ड नोडहरू छन्, पहिले तिनीहरू हटाउनुहोस्।',
'agent_node_has_users_cannot_delete' => 'यस एजेन्ट नोडमा अझै एडमिन खाता जोडिएको छ, मेटाउन मिल्दैन।',
'agent_node_has_roles_cannot_delete' => 'यस एजेन्ट नोडमा अझै भूमिका जोडिएको छ, मेटाउन मिल्दैन।',
'agent_role_in_use' => 'यो भूमिका अझै :count खातामा प्रयोगमा छ। पहिले खाता ट्याबमा हटाउनुहोस्।',
'agent_role_read_only' => 'Read-only टेम्प्लेट भूमिका मेटाउन वा सम्पादन गर्न मिल्दैन।',
'user_cannot_delete_self' => 'आफ्नै खाता मेटाउन मिल्दैन।',
'user_cannot_delete_last_super_admin' => 'अन्तिम सुपर एडमिन मेटाउन मिल्दैन।',
'super_admin_only_for_roles' => 'भूमिका व्यवस्थापन केवल सुपर एडमिनले गर्न सक्छ।',

3
lang/ne/validation.php Normal file
View File

@@ -0,0 +1,3 @@
<?php
return require __DIR__.'/../zh/validation.php';

View File

@@ -23,6 +23,8 @@ return [
'agent_node_has_children_cannot_delete' => '该代理节点存在下级代理,请先清空下级后再删除。',
'agent_node_has_users_cannot_delete' => '该代理节点下仍有关联账号,不能删除。',
'agent_node_has_roles_cannot_delete' => '该代理节点下仍有关联角色,不能删除。',
'agent_role_in_use' => '该角色仍有 :count 个账号在使用,请先在「账号」里解除绑定后再删除。',
'agent_role_read_only' => '只读模板角色不可删除或修改。',
'user_cannot_delete_self' => '不能删除当前登录账号。',
'user_cannot_delete_last_super_admin' => '不能删除最后一个超级管理员。',
'super_admin_only_for_roles' => '仅超级管理员可管理角色。',

11
lang/zh/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,162 @@
<?php
/** 全站 API 校验字段中文名FormRequest / 控制器 validate 共用) */
return [
'account' => '登录账号',
'password' => '密码',
'captcha_key' => '验证码',
'captcha_code' => '验证码',
'username' => '用户名',
'nickname' => '昵称',
'name' => '名称',
'code' => '编码',
'slug' => '标识',
'status' => '状态',
'email' => '邮箱',
'group' => '配置分组',
'value' => '配置值',
'key' => '配置键',
'items' => '配置项列表',
'parent_id' => '上级节点',
'role_ids' => '角色',
'role_ids.*' => '角色',
'role_slugs' => '角色',
'role_slugs.*' => '角色',
'permission_slugs' => '权限',
'permission_slugs.*' => '权限项',
'permissions' => '权限',
'permissions.*' => '权限项',
'grants' => '下放权限',
'grants.*.menu_action_id' => '权限项',
'grants.*.can_delegate' => '是否可继续下放',
'child_agent_id' => '下级代理',
'role' => '角色',
'user' => '账号',
'site_id' => '站点',
'site_code' => '站点编码',
'site_player_id' => '主站玩家 ID',
'player_id' => '玩家',
'player_account' => '玩家账号',
'agent_node_id' => '代理节点',
'currency_code' => '币种',
'currency' => '币种',
'amount' => '金额',
'amount_delta' => '调整金额',
'reason' => '原因',
'remark' => '备注',
'draw_id' => '期号 ID',
'draw_no' => '期号',
'draw_time' => '开奖时间',
'start_time' => '开始时间',
'close_time' => '封盘时间',
'business_date' => '业务日期',
'sequence_no' => '流水号',
'period' => '统计周期',
'date_from' => '开始日期',
'date_to' => '结束日期',
'metric' => '指标',
'play_code' => '玩法',
'keyword' => '关键词',
'default_currency' => '默认币种',
'page' => '页码',
'per_page' => '每页条数',
'size' => '每页条数',
'number' => '号码',
'start_date' => '开始日期',
'end_date' => '结束日期',
'transfer_no' => '转账单号',
'external_ref_no' => '外部参考号',
'txn_no' => '流水号',
'biz_type' => '业务类型',
'created_from' => '创建开始日期',
'created_to' => '创建结束日期',
'report_type' => '报表类型',
'export_format' => '导出格式',
'parameters' => '报表参数',
'parameters.date_from' => '开始日期',
'parameters.date_to' => '结束日期',
'parameters.player_id' => '玩家',
'parameters.play_code' => '玩法',
'parameters.operator_id' => '操作员',
'parameters.draw_id' => '期号',
'parameters.draw_no' => '期号',
'parameters.normalized_number' => '号码',
'filter_json' => '筛选条件',
'filter_json.date_from' => '开始日期',
'filter_json.date_to' => '结束日期',
'filter_json.draw_id' => '期号',
'filter_json.draw_no' => '期号',
'filter_json.normalized_number' => '号码',
'reconcile_type' => '对账类型',
'period_start' => '周期开始',
'period_end' => '周期结束',
'decimal_places' => '小数位数',
'is_enabled' => '是否启用',
'is_bettable' => '是否可下注',
'description' => '描述',
'wallet_api_url' => '钱包 API 地址',
'wallet_debit_path' => '扣款路径',
'wallet_credit_path' => '加款路径',
'wallet_balance_path' => '余额查询路径',
'wallet_timeout_seconds' => '请求超时(秒)',
'iframe_allowed_origins' => 'iframe 允许来源',
'iframe_allowed_origins.*' => 'iframe 来源',
'lottery_h5_base_url' => 'H5 基础地址',
'notes' => '备注',
'clone_from_version_id' => '克隆来源版本',
'current_amount' => '当前金额',
'contribution_rate' => '贡献比例',
'trigger_threshold' => '触发阈值',
'payout_rate' => '派彩比例',
'force_trigger_draw_gap' => '强制触发期数间隔',
'min_bet_amount' => '最小下注额',
'combo_trigger_play_codes' => '组合触发玩法',
'combo_trigger_play_codes.*' => '组合触发玩法',
'lines' => '注单明细',
'lines.*.number' => '投注号码',
'lines.*.play_code' => '玩法',
'lines.*.amount' => '下注金额',
'lines.*.digit_slot' => '数位',
'lines.*.dimension' => '维度',
'client_trace_id' => '客户端追踪 ID',
'idempotent_key' => '幂等键',
'expected_config_versions' => '期望配置版本',
'expected_config_versions.play_config_version_no' => '玩法配置版本号',
'expected_config_versions.odds_version_no' => '赔率配置版本号',
'expected_config_versions.risk_cap_version_no' => '封顶配置版本号',
'prize_type' => '奖项类型',
'prize_index' => '奖项序号',
'number_4d' => '四位号码',
'normalized_number' => '号码',
'items.*.key' => '配置键',
'items.*.value' => '配置值',
'items.*.play_code' => '玩法',
'items.*.prize_scope' => '奖项档位',
'items.*.odds_value' => '赔率',
'items.*.rebate_rate' => '返水比例',
'items.*.commission_rate' => '佣金比例',
'items.*.currency_code' => '币种',
'items.*.category' => '分类',
'items.*.dimension' => '维度',
'items.*.bet_mode' => '下注模式',
'items.*.display_name' => '显示名称',
'items.*.display_order' => '排序',
'items.*.min_bet_amount' => '最小下注额',
'items.*.max_bet_amount' => '最大下注额',
'items.*.supports_multi_number' => '是否支持多号',
'items.*.cap_amount' => '封顶金额',
'items.*.cap_type' => '封顶类型',
'items.*.draw_id' => '期号',
'items.*.prize_type' => '奖项类型',
'items.*.prize_index' => '奖项序号',
'items.*.number_4d' => '四位号码',
'items.*.normalized_number' => '号码',
'items.*.side_a_ref' => '对账侧 A 参考',
'items.*.side_b_ref' => '对账侧 B 参考',
'items.*.difference_amount' => '差异金额',
'items.*.status' => '状态',
'sort_order' => '排序',
'supports_multi_number' => '是否支持多号',
'reserved_rule_json' => '预留规则',
'extra_config_json' => '扩展配置',
];

View File

@@ -0,0 +1,17 @@
<?php
/** 业务层 ValidationException 简写键 */
return [
'unique' => '该内容已存在,请更换后重试。',
'required' => ':attribute 不能为空。',
'system_role' => '系统角色不可删除。',
'agent_mismatch' => '该账号不属于当前代理节点。',
'invalid_for_agent' => '所选角色与当前代理不匹配。',
'not_manageable' => '无权管理该下级代理。',
'invalid_menu_action' => '权限项无效或不存在。',
'exceeds_actor' => '下列权限超出您可分配的范围::detail',
'exceeds_parent_ceiling' => '下列权限超出上级允许下放的范围::detail',
'exceeds_delegation_ceiling' => '下列权限超出本节点下放上限::detail',
'permission_exceeds_actor' => '下列权限超出您可分配的范围::detail',
'permission_catalog_incomplete' => '权限目录不完整,缺少::detail。请联系管理员执行 migrate 与 admin-auth-sync。',
];

View File

@@ -0,0 +1,60 @@
<?php
/** 按字段定制的格式/唯一性说明 */
return [
'code' => [
'regex' => '编码只能使用字母、数字、下划线_和连字符-);站点编码须以小写字母或数字开头。',
'unique' => '该编码已被占用,请更换。',
],
'slug' => [
'regex' => '标识只能使用小写字母、数字、下划线和连字符。',
'unique' => '该标识已被占用,请更换。',
],
'account' => [
'regex' => '登录账号只能使用字母、数字、点(.)、下划线和连字符。',
],
'username' => [
'regex' => '用户名只能使用字母、数字、点(.)、下划线和连字符。',
'unique' => '该用户名已被占用,请更换。',
],
'email' => [
'unique' => '该邮箱已被占用,请更换。',
],
'draw_no' => [
'regex' => '期号格式须为 YYYYMMDD-流水号(例如 20260101-001。',
],
'number_4d' => [
'regex' => '号码必须是 4 位数字。',
],
'normalized_number' => [
'regex' => '号码必须是 4 位数字。',
'size' => '号码必须是 4 位数字。',
],
'wallet_api_url' => [
'wallet_api_url' => '钱包 API 地址必须是 https 的公开域名根地址,不能为 localhost、内网 IP且不能带路径或查询参数。',
],
'amount_delta' => [
'not_in' => '调整金额不能为 0。',
],
'password' => [
'min' => '密码至少需要 :min 个字符。',
],
'reason' => [
'min' => '原因至少需要 :min 个字符。',
],
'lines' => [
'max' => '单次最多提交 :max 注。',
'min' => '至少需要提交 :min 注。',
],
'items' => [
'min' => '至少需要 :min 条配置。',
'max' => '最多只能提交 :max 条配置。',
'size' => '必须恰好包含 :size 条配置。',
],
'role_slugs' => [
'min' => '至少须选择一个角色。',
],
'role_ids' => [
'required' => '请选择角色。',
],
];

View File

@@ -0,0 +1,7 @@
<?php
/** 代码中写死的英文校验句 → 中文(按原文精确匹配) */
return [
'items must contain the complete 23 draw prize slots.' => '须提交完整的 23 个开奖奖项(头奖、二奖、三奖、入围、安慰奖各档位)。',
'items_must_contain_23_slots' => '须提交完整的 23 个开奖奖项(头奖、二奖、三奖、入围、安慰奖各档位)。',
];

View File

@@ -0,0 +1,113 @@
<?php
/** Laravel 标准校验规则中文文案 */
return [
'accepted' => '请勾选接受 :attribute。',
'accepted_if' => '当 :other 为 :value 时,必须接受 :attribute。',
'active_url' => ':attribute 必须是有效的网址。',
'after' => ':attribute 必须晚于 :date。',
'after_or_equal' => ':attribute 必须不早于 :date。',
'alpha' => ':attribute 只能包含字母。',
'alpha_dash' => ':attribute 只能包含字母、数字、下划线和连字符。',
'alpha_num' => ':attribute 只能包含字母和数字。',
'array' => ':attribute 必须是列表。',
'ascii' => ':attribute 只能包含半角字母、数字和符号。',
'before' => ':attribute 必须早于 :date。',
'before_or_equal' => ':attribute 必须不晚于 :date。',
'between' => [
'array' => ':attribute 项数须在 :min 到 :max 之间。',
'file' => ':attribute 大小须在 :min 到 :max KB 之间。',
'numeric' => ':attribute 须在 :min 到 :max 之间。',
'string' => ':attribute 长度须在 :min 到 :max 个字符之间。',
],
'boolean' => ':attribute 必须是是或否。',
'confirmed' => ':attribute 两次输入不一致。',
'date' => ':attribute 必须是有效日期。',
'date_equals' => ':attribute 必须等于 :date。',
'date_format' => ':attribute 格式须为 :format。',
'decimal' => ':attribute 须保留 :decimal 位小数。',
'declined' => '必须拒绝 :attribute。',
'different' => ':attribute 与 :other 不能相同。',
'digits' => ':attribute 必须是 :digits 位数字。',
'digits_between' => ':attribute 须在 :min 到 :max 位数字之间。',
'distinct' => ':attribute 存在重复值。',
'email' => ':attribute 必须是有效的邮箱地址。',
'ends_with' => ':attribute 必须以以下之一结尾::values。',
'enum' => '所选的 :attribute 无效。',
'exists' => '所选的 :attribute 不存在或不可用。',
'file' => ':attribute 必须是文件。',
'filled' => ':attribute 不能为空。',
'gt' => [
'array' => ':attribute 项数须多于 :value 个。',
'file' => ':attribute 须大于 :value KB。',
'numeric' => ':attribute 须大于 :value。',
'string' => ':attribute 须多于 :value 个字符。',
],
'gte' => [
'array' => ':attribute 至少须有 :value 项。',
'file' => ':attribute 须不小于 :value KB。',
'numeric' => ':attribute 须不小于 :value。',
'string' => ':attribute 至少须 :value 个字符。',
],
'image' => ':attribute 必须是图片。',
'in' => ':attribute 取值无效。',
'in_array' => ':attribute 不存在于 :other 中。',
'integer' => ':attribute 必须是整数。',
'ip' => ':attribute 必须是有效的 IP 地址。',
'json' => ':attribute 必须是有效的 JSON。',
'lowercase' => ':attribute 必须是小写。',
'lt' => [
'array' => ':attribute 项数须少于 :value 个。',
'file' => ':attribute 须小于 :value KB。',
'numeric' => ':attribute 须小于 :value。',
'string' => ':attribute 须少于 :value 个字符。',
],
'lte' => [
'array' => ':attribute 最多 :value 项。',
'file' => ':attribute 须不大于 :value KB。',
'numeric' => ':attribute 须不大于 :value。',
'string' => ':attribute 最多 :value 个字符。',
],
'max' => [
'array' => ':attribute 最多 :max 项。',
'file' => ':attribute 不能超过 :max KB。',
'numeric' => ':attribute 不能大于 :max。',
'string' => ':attribute 不能超过 :max 个字符。',
],
'min' => [
'array' => ':attribute 至少 :min 项。',
'file' => ':attribute 至少 :min KB。',
'numeric' => ':attribute 不能小于 :min。',
'string' => ':attribute 至少 :min 个字符。',
],
'not_in' => ':attribute 取值无效。',
'not_regex' => ':attribute 格式不正确。',
'numeric' => ':attribute 必须是数字。',
'present' => ':attribute 必须提交(可为空)。',
'prohibited' => ':attribute 不允许提交。',
'prohibited_if' => '当 :other 为 :value 时,不允许提交 :attribute。',
'regex' => ':attribute 格式不正确。',
'required' => ':attribute 不能为空。',
'required_if' => '当 :other 为 :value 时,:attribute 不能为空。',
'required_unless' => '除非 :other 在 :values 中,否则 :attribute 不能为空。',
'required_with' => '当存在 :values 时,:attribute 不能为空。',
'required_with_all' => '当存在 :values 时,:attribute 不能为空。',
'required_without' => '当不存在 :values 时,:attribute 不能为空。',
'required_without_all' => '当 :values 均不存在时,:attribute 不能为空。',
'required_array_keys' => ':attribute 须包含::values。',
'same' => ':attribute 必须与 :other 一致。',
'size' => [
'array' => ':attribute 必须包含 :size 项。',
'file' => ':attribute 必须为 :size KB。',
'numeric' => ':attribute 必须为 :size。',
'string' => ':attribute 必须为 :size 个字符。',
],
'starts_with' => ':attribute 必须以以下之一开头::values。',
'string' => ':attribute 必须是文本。',
'timezone' => ':attribute 必须是有效的时区。',
'unique' => ':attribute 已被占用,请更换。',
'uploaded' => ':attribute 上传失败。',
'uppercase' => ':attribute 必须是大写。',
'url' => ':attribute 必须是有效的网址。',
'uuid' => ':attribute 必须是有效的 UUID。',
];