From e6cf94af46260f854b5651d8ff68b812501b3843 Mon Sep 17 00:00:00 2001 From: kang Date: Mon, 1 Jun 2026 14:23:48 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E4=BD=BF=E7=94=A8=20ApiMessage=20?= =?UTF-8?q?=E7=BB=9F=E4=B8=80=E9=94=99=E8=AF=AF=E5=93=8D=E5=BA=94=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在多个控制器中引入 ApiMessage,替换原有的 ApiResponse 错误处理逻辑,确保错误信息的一致性与可读性。 - 更新错误返回信息,使用更具语义的键值,提升 API 的可维护性与用户体验。 - 适配相关控制器的请求参数,确保在处理错误时能够正确返回相应的错误信息。 --- .../Config/OddsItemsReplaceController.php | 3 +- .../Config/OddsVersionDestroyController.php | 3 +- .../Config/OddsVersionPublishController.php | 3 +- .../PlayConfigItemsReplaceController.php | 3 +- .../PlayConfigVersionDestroyController.php | 3 +- .../PlayConfigVersionPublishController.php | 8 +- .../Config/RiskCapItemsReplaceController.php | 3 +- .../RiskCapVersionDestroyController.php | 3 +- .../RiskCapVersionPublishController.php | 3 +- .../AdminCurrencyDestroyController.php | 17 +-- .../Draw/AdminDrawBatchDestroyController.php | 14 +- .../Admin/Draw/AdminDrawDestroyController.php | 13 +- .../Admin/Draw/AdminDrawStoreController.php | 9 +- .../Admin/Draw/AdminDrawUpdateController.php | 12 +- .../V1/Admin/Draw/DrawCancelController.php | 8 +- .../Admin/Draw/DrawManualCloseController.php | 8 +- .../DrawManualResultBatchStoreController.php | 10 +- .../V1/Admin/Draw/DrawReopenController.php | 10 +- .../Draw/DrawResultBatchPublishController.php | 10 +- .../V1/Admin/Draw/DrawRngRunController.php | 5 +- ...egrationSiteConnectivityTestController.php | 3 +- .../AdminIntegrationSiteExportController.php | 3 +- ...IntegrationSiteRotateSecretsController.php | 3 +- .../AdminIntegrationSiteShowController.php | 3 +- .../AdminIntegrationSiteUpdateController.php | 3 +- .../AdminJackpotPoolAdjustController.php | 10 +- .../AdminJackpotPoolManualBurstController.php | 5 +- .../Player/AdminPlayerDestroyController.php | 15 +- .../Player/AdminPlayerStoreController.php | 15 +- .../AdminRiskPoolManualStatusController.php | 12 +- .../AdminSettlementBatchApproveController.php | 5 +- .../AdminSettlementBatchPayoutController.php | 12 +- .../AdminSettlementBatchRejectController.php | 5 +- .../Admin/User/AdminRoleDestroyController.php | 7 +- .../Admin/User/AdminUserDestroyController.php | 15 +- .../User/Concerns/EnsuresSuperAdminActor.php | 8 +- .../TransferOrderReconcileController.php | 8 +- .../Play/PlayEffectiveCatalogController.php | 15 +- .../Api/V1/Setting/SettingIndexController.php | 4 +- .../Api/V1/Wallet/WalletBalanceController.php | 3 +- .../V1/Wallet/WalletTransferInController.php | 3 +- .../V1/Wallet/WalletTransferOutController.php | 3 +- .../EnsureAdminApiResourcePermission.php | 15 +- app/Support/AdminMessage.php | 14 +- app/Support/AdminSiteScope.php | 10 +- app/Support/ApiMessage.php | 140 ++++++++++++++++++ app/Support/ApiResponse.php | 5 +- app/Support/LotteryMessage.php | 23 +-- lang/en/admin.php | 20 +++ lang/en/api.php | 40 +++++ lang/en/wallet.php | 1 + lang/ne/admin.php | 30 +++- lang/ne/api.php | 40 +++++ lang/ne/wallet.php | 1 + lang/zh/admin.php | 20 +++ lang/zh/api.php | 40 +++++ lang/zh/wallet.php | 1 + tests/Feature/AdminUserPermissionApiTest.php | 10 +- tests/Feature/ApiMessageTest.php | 30 ++++ 59 files changed, 518 insertions(+), 230 deletions(-) create mode 100644 app/Support/ApiMessage.php create mode 100644 tests/Feature/ApiMessageTest.php diff --git a/app/Http/Controllers/Api/V1/Admin/Config/OddsItemsReplaceController.php b/app/Http/Controllers/Api/V1/Admin/Config/OddsItemsReplaceController.php index 4f15a46..491d555 100644 --- a/app/Http/Controllers/Api/V1/Admin/Config/OddsItemsReplaceController.php +++ b/app/Http/Controllers/Api/V1/Admin/Config/OddsItemsReplaceController.php @@ -5,6 +5,7 @@ namespace App\Http\Controllers\Api\V1\Admin\Config; use App\Models\AdminUser; use App\Lottery\ErrorCode; use App\Models\OddsVersion; +use App\Support\ApiMessage; use App\Support\ApiResponse; use Illuminate\Http\Request; use Illuminate\Validation\Rule; @@ -27,7 +28,7 @@ final class OddsItemsReplaceController extends Controller if ($version->status !== ConfigVersionStatus::Draft->value) { return ApiResponse::error( - 'version is not draft', + ApiMessage::get($request, 'config_version_not_draft'), ErrorCode::ConfigVersionNotDraft->value, null, 400, diff --git a/app/Http/Controllers/Api/V1/Admin/Config/OddsVersionDestroyController.php b/app/Http/Controllers/Api/V1/Admin/Config/OddsVersionDestroyController.php index a1b652d..a9b9d4a 100644 --- a/app/Http/Controllers/Api/V1/Admin/Config/OddsVersionDestroyController.php +++ b/app/Http/Controllers/Api/V1/Admin/Config/OddsVersionDestroyController.php @@ -5,6 +5,7 @@ namespace App\Http\Controllers\Api\V1\Admin\Config; use App\Models\AdminUser; use App\Models\OddsVersion; use App\Lottery\ErrorCode; +use App\Support\ApiMessage; use App\Support\ApiResponse; use Illuminate\Http\Request; use Illuminate\Http\JsonResponse; @@ -25,7 +26,7 @@ final class OddsVersionDestroyController extends Controller if ($version->status === ConfigVersionStatus::Active->value) { return ApiResponse::error( - 'cannot delete active config version', + ApiMessage::get($request, 'config_version_cannot_delete_active'), ErrorCode::ConfigVersionCannotDeleteActive->value, null, 400, diff --git a/app/Http/Controllers/Api/V1/Admin/Config/OddsVersionPublishController.php b/app/Http/Controllers/Api/V1/Admin/Config/OddsVersionPublishController.php index 7e14f43..c1ab893 100644 --- a/app/Http/Controllers/Api/V1/Admin/Config/OddsVersionPublishController.php +++ b/app/Http/Controllers/Api/V1/Admin/Config/OddsVersionPublishController.php @@ -5,6 +5,7 @@ namespace App\Http\Controllers\Api\V1\Admin\Config; use App\Models\AdminUser; use App\Lottery\ErrorCode; use App\Models\OddsVersion; +use App\Support\ApiMessage; use App\Support\ApiResponse; use Illuminate\Http\Request; use Illuminate\Http\JsonResponse; @@ -26,7 +27,7 @@ final class OddsVersionPublishController extends Controller if ($version->status !== ConfigVersionStatus::Draft->value) { return ApiResponse::error( - 'version is not draft', + ApiMessage::get($request, 'config_version_not_draft'), ErrorCode::ConfigVersionNotDraft->value, null, 400, diff --git a/app/Http/Controllers/Api/V1/Admin/Config/PlayConfigItemsReplaceController.php b/app/Http/Controllers/Api/V1/Admin/Config/PlayConfigItemsReplaceController.php index 92853c9..9be8cb6 100644 --- a/app/Http/Controllers/Api/V1/Admin/Config/PlayConfigItemsReplaceController.php +++ b/app/Http/Controllers/Api/V1/Admin/Config/PlayConfigItemsReplaceController.php @@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api\V1\Admin\Config; use App\Models\AdminUser; use App\Lottery\ErrorCode; +use App\Support\ApiMessage; use App\Support\ApiResponse; use Illuminate\Http\Request; use Illuminate\Validation\Rule; @@ -27,7 +28,7 @@ final class PlayConfigItemsReplaceController extends Controller if ($version->status !== ConfigVersionStatus::Draft->value) { return ApiResponse::error( - 'version is not draft', + ApiMessage::get($request, 'config_version_not_draft'), ErrorCode::ConfigVersionNotDraft->value, null, 400, diff --git a/app/Http/Controllers/Api/V1/Admin/Config/PlayConfigVersionDestroyController.php b/app/Http/Controllers/Api/V1/Admin/Config/PlayConfigVersionDestroyController.php index 54b71e7..4cefd3e 100644 --- a/app/Http/Controllers/Api/V1/Admin/Config/PlayConfigVersionDestroyController.php +++ b/app/Http/Controllers/Api/V1/Admin/Config/PlayConfigVersionDestroyController.php @@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api\V1\Admin\Config; use App\Models\AdminUser; use App\Lottery\ErrorCode; +use App\Support\ApiMessage; use App\Support\ApiResponse; use Illuminate\Http\Request; use App\Models\PlayConfigVersion; @@ -25,7 +26,7 @@ final class PlayConfigVersionDestroyController extends Controller if ($version->status === ConfigVersionStatus::Active->value) { return ApiResponse::error( - 'cannot delete active config version', + ApiMessage::get($request, 'config_version_cannot_delete_active'), ErrorCode::ConfigVersionCannotDeleteActive->value, null, 400, diff --git a/app/Http/Controllers/Api/V1/Admin/Config/PlayConfigVersionPublishController.php b/app/Http/Controllers/Api/V1/Admin/Config/PlayConfigVersionPublishController.php index 324a4d7..d7ea9c6 100644 --- a/app/Http/Controllers/Api/V1/Admin/Config/PlayConfigVersionPublishController.php +++ b/app/Http/Controllers/Api/V1/Admin/Config/PlayConfigVersionPublishController.php @@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api\V1\Admin\Config; use App\Models\AdminUser; use App\Lottery\ErrorCode; +use App\Support\ApiMessage; use App\Support\ApiResponse; use Illuminate\Http\Request; use App\Models\PlayConfigVersion; @@ -25,12 +26,7 @@ final class PlayConfigVersionPublishController extends Controller $version = PlayConfigVersion::query()->whereKey($id)->firstOrFail(); if ($version->status !== ConfigVersionStatus::Draft->value) { - return ApiResponse::error( - 'version is not draft', - ErrorCode::ConfigVersionNotDraft->value, - null, - 400, - ); + return ApiMessage::errorResponse($request, 'config_version_not_draft', ErrorCode::ConfigVersionNotDraft->value, null, 400); } $service->publish($version, $admin, $request); diff --git a/app/Http/Controllers/Api/V1/Admin/Config/RiskCapItemsReplaceController.php b/app/Http/Controllers/Api/V1/Admin/Config/RiskCapItemsReplaceController.php index 883990d..abc20b0 100644 --- a/app/Http/Controllers/Api/V1/Admin/Config/RiskCapItemsReplaceController.php +++ b/app/Http/Controllers/Api/V1/Admin/Config/RiskCapItemsReplaceController.php @@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api\V1\Admin\Config; use App\Models\AdminUser; use App\Lottery\ErrorCode; +use App\Support\ApiMessage; use App\Support\ApiResponse; use Illuminate\Http\Request; use App\Models\RiskCapVersion; @@ -26,7 +27,7 @@ final class RiskCapItemsReplaceController extends Controller if ($version->status !== ConfigVersionStatus::Draft->value) { return ApiResponse::error( - 'version is not draft', + ApiMessage::get($request, 'config_version_not_draft'), ErrorCode::ConfigVersionNotDraft->value, null, 400, diff --git a/app/Http/Controllers/Api/V1/Admin/Config/RiskCapVersionDestroyController.php b/app/Http/Controllers/Api/V1/Admin/Config/RiskCapVersionDestroyController.php index c5fbf97..8e4be88 100644 --- a/app/Http/Controllers/Api/V1/Admin/Config/RiskCapVersionDestroyController.php +++ b/app/Http/Controllers/Api/V1/Admin/Config/RiskCapVersionDestroyController.php @@ -5,6 +5,7 @@ namespace App\Http\Controllers\Api\V1\Admin\Config; use App\Models\AdminUser; use App\Models\RiskCapVersion; use App\Lottery\ErrorCode; +use App\Support\ApiMessage; use App\Support\ApiResponse; use Illuminate\Http\Request; use Illuminate\Http\JsonResponse; @@ -25,7 +26,7 @@ final class RiskCapVersionDestroyController extends Controller if ($version->status === ConfigVersionStatus::Active->value) { return ApiResponse::error( - 'cannot delete active config version', + ApiMessage::get($request, 'config_version_cannot_delete_active'), ErrorCode::ConfigVersionCannotDeleteActive->value, null, 400, diff --git a/app/Http/Controllers/Api/V1/Admin/Config/RiskCapVersionPublishController.php b/app/Http/Controllers/Api/V1/Admin/Config/RiskCapVersionPublishController.php index 600f90c..a4ef688 100644 --- a/app/Http/Controllers/Api/V1/Admin/Config/RiskCapVersionPublishController.php +++ b/app/Http/Controllers/Api/V1/Admin/Config/RiskCapVersionPublishController.php @@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api\V1\Admin\Config; use App\Models\AdminUser; use App\Lottery\ErrorCode; +use App\Support\ApiMessage; use App\Support\ApiResponse; use Illuminate\Http\Request; use App\Models\RiskCapVersion; @@ -26,7 +27,7 @@ final class RiskCapVersionPublishController extends Controller if ($version->status !== ConfigVersionStatus::Draft->value) { return ApiResponse::error( - 'version is not draft', + ApiMessage::get($request, 'config_version_not_draft'), ErrorCode::ConfigVersionNotDraft->value, null, 400, diff --git a/app/Http/Controllers/Api/V1/Admin/Currency/AdminCurrencyDestroyController.php b/app/Http/Controllers/Api/V1/Admin/Currency/AdminCurrencyDestroyController.php index 5c86132..42eb8f9 100644 --- a/app/Http/Controllers/Api/V1/Admin/Currency/AdminCurrencyDestroyController.php +++ b/app/Http/Controllers/Api/V1/Admin/Currency/AdminCurrencyDestroyController.php @@ -4,7 +4,9 @@ namespace App\Http\Controllers\Api\V1\Admin\Currency; use App\Models\Currency; use App\Lottery\ErrorCode; +use App\Support\ApiMessage; use App\Support\ApiResponse; +use Illuminate\Http\Request; use Illuminate\Http\JsonResponse; use Illuminate\Support\Facades\DB; use App\Services\LotterySettings; @@ -12,26 +14,23 @@ use App\Http\Controllers\Controller; final class AdminCurrencyDestroyController extends Controller { - public function __invoke(Currency $currency): JsonResponse + public function __invoke(Request $request, Currency $currency): JsonResponse { $code = strtoupper((string) $currency->code); if ($code === LotterySettings::defaultCurrency()) { - return ApiResponse::error( - '默认币种不可删除', - ErrorCode::ValidationFailed->value, - null, - 422, - ); + return ApiMessage::errorResponse($request, 'admin.currency_default_cannot_delete', ErrorCode::ValidationFailed->value, null, 422); } $references = $this->referenceSummary($code); if ($references !== []) { - return ApiResponse::error( - '该币种已被业务数据引用,暂不可删除:'.implode('、', $references), + return ApiMessage::errorResponse( + $request, + 'admin.currency_referenced_cannot_delete', ErrorCode::ValidationFailed->value, ['references' => $references], 422, + ['refs' => implode('、', $references)], ); } diff --git a/app/Http/Controllers/Api/V1/Admin/Draw/AdminDrawBatchDestroyController.php b/app/Http/Controllers/Api/V1/Admin/Draw/AdminDrawBatchDestroyController.php index e9f2db0..b70f245 100644 --- a/app/Http/Controllers/Api/V1/Admin/Draw/AdminDrawBatchDestroyController.php +++ b/app/Http/Controllers/Api/V1/Admin/Draw/AdminDrawBatchDestroyController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers\Api\V1\Admin\Draw; use App\Lottery\ErrorCode; +use App\Support\ApiMessage; use App\Support\ApiResponse; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; @@ -20,7 +21,7 @@ final class AdminDrawBatchDestroyController extends Controller $drawIds = $request->input('draw_ids', []); if (!is_array($drawIds) || empty($drawIds)) { - return ApiResponse::error(trans('api.invalid_params'), ErrorCode::ClientHttpError->value, [], 400); + return ApiMessage::errorResponse($request, 'invalid_params', ErrorCode::ClientHttpError->value, [], 400); } $results = [ @@ -36,17 +37,14 @@ final class AdminDrawBatchDestroyController extends Controller } catch (\RuntimeException $e) { $results['failed'][] = [ 'id' => $drawId, - 'reason' => match ($e->getMessage()) { - 'draw_not_deletable' => trans('api.draw_not_deletable'), - 'draw_has_bets' => trans('api.draw_has_bets'), - 'draw_result_exists' => trans('api.draw_result_exists'), - default => trans('api.client_error'), - }, + 'reason' => ApiMessage::reason($request, $e->getMessage()), + 'reason_key' => $e->getMessage(), ]; } catch (\Illuminate\Database\Eloquent\ModelNotFoundException $e) { $results['failed'][] = [ 'id' => $drawId, - 'reason' => trans('api.draw_not_found'), + 'reason' => ApiMessage::get($request, 'draw_not_found'), + 'reason_key' => 'draw_not_found', ]; } } diff --git a/app/Http/Controllers/Api/V1/Admin/Draw/AdminDrawDestroyController.php b/app/Http/Controllers/Api/V1/Admin/Draw/AdminDrawDestroyController.php index 873ff7c..ba02eb0 100644 --- a/app/Http/Controllers/Api/V1/Admin/Draw/AdminDrawDestroyController.php +++ b/app/Http/Controllers/Api/V1/Admin/Draw/AdminDrawDestroyController.php @@ -5,6 +5,8 @@ namespace App\Http\Controllers\Api\V1\Admin\Draw; use App\Models\Draw; use App\Lottery\ErrorCode; use App\Support\ApiResponse; +use App\Support\ApiMessage; +use Illuminate\Http\Request; use Illuminate\Http\JsonResponse; use App\Http\Controllers\Controller; use App\Services\Draw\DrawDestroyService; @@ -15,19 +17,12 @@ final class AdminDrawDestroyController extends Controller private readonly DrawDestroyService $service, ) {} - public function __invoke(Draw $draw): JsonResponse + public function __invoke(Request $request, Draw $draw): JsonResponse { try { $this->service->destroy($draw); } catch (\RuntimeException $e) { - $message = match ($e->getMessage()) { - 'draw_not_deletable' => trans('api.draw_not_deletable'), - 'draw_has_bets' => trans('api.draw_has_bets'), - 'draw_result_exists' => trans('api.draw_result_exists'), - default => trans('api.client_error'), - }; - - return ApiResponse::error($message, ErrorCode::ClientHttpError->value, ['reason' => $e->getMessage()], 409); + return ApiMessage::runtimeErrorResponse($request, $e); } return ApiResponse::success(['deleted' => true]); diff --git a/app/Http/Controllers/Api/V1/Admin/Draw/AdminDrawStoreController.php b/app/Http/Controllers/Api/V1/Admin/Draw/AdminDrawStoreController.php index a3cf412..86a2436 100644 --- a/app/Http/Controllers/Api/V1/Admin/Draw/AdminDrawStoreController.php +++ b/app/Http/Controllers/Api/V1/Admin/Draw/AdminDrawStoreController.php @@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api\V1\Admin\Draw; use App\Lottery\ErrorCode; use App\Support\ApiResponse; +use App\Support\ApiMessage; use Illuminate\Http\JsonResponse; use App\Http\Controllers\Controller; use App\Services\Draw\DrawManualCreateService; @@ -20,13 +21,7 @@ final class AdminDrawStoreController extends Controller try { $draw = $this->service->create($request->validated()); } catch (\RuntimeException $e) { - $message = match ($e->getMessage()) { - 'draw_no_exists' => trans('api.draw_no_exists'), - 'draw_timeline_invalid' => trans('api.draw_timeline_invalid'), - default => trans('api.client_error'), - }; - - return ApiResponse::error($message, ErrorCode::ClientHttpError->value, ['reason' => $e->getMessage()], 409); + return ApiMessage::runtimeErrorResponse($request, $e); } return ApiResponse::success([ diff --git a/app/Http/Controllers/Api/V1/Admin/Draw/AdminDrawUpdateController.php b/app/Http/Controllers/Api/V1/Admin/Draw/AdminDrawUpdateController.php index 80d5ed3..843a3d1 100644 --- a/app/Http/Controllers/Api/V1/Admin/Draw/AdminDrawUpdateController.php +++ b/app/Http/Controllers/Api/V1/Admin/Draw/AdminDrawUpdateController.php @@ -5,6 +5,7 @@ namespace App\Http\Controllers\Api\V1\Admin\Draw; use App\Models\Draw; use App\Lottery\ErrorCode; use App\Support\ApiResponse; +use App\Support\ApiMessage; use Illuminate\Http\JsonResponse; use App\Http\Controllers\Controller; use App\Http\Requests\Admin\DrawStoreRequest; @@ -21,16 +22,7 @@ final class AdminDrawUpdateController extends Controller try { $updated = $this->service->update($draw, $request->validated()); } catch (\RuntimeException $e) { - $message = match ($e->getMessage()) { - 'draw_no_exists' => trans('api.draw_no_exists'), - 'draw_timeline_invalid' => trans('api.draw_timeline_invalid'), - 'draw_not_editable' => trans('api.draw_not_editable'), - 'draw_has_bets' => trans('api.draw_has_bets'), - 'draw_result_exists' => trans('api.draw_result_exists'), - default => trans('api.client_error'), - }; - - return ApiResponse::error($message, ErrorCode::ClientHttpError->value, ['reason' => $e->getMessage()], 409); + return ApiMessage::runtimeErrorResponse($request, $e); } return ApiResponse::success([ diff --git a/app/Http/Controllers/Api/V1/Admin/Draw/DrawCancelController.php b/app/Http/Controllers/Api/V1/Admin/Draw/DrawCancelController.php index 721a93a..8446614 100644 --- a/app/Http/Controllers/Api/V1/Admin/Draw/DrawCancelController.php +++ b/app/Http/Controllers/Api/V1/Admin/Draw/DrawCancelController.php @@ -4,7 +4,9 @@ namespace App\Http\Controllers\Api\V1\Admin\Draw; use App\Models\Draw; use App\Lottery\ErrorCode; +use App\Support\ApiMessage; use App\Support\ApiResponse; +use Illuminate\Http\Request; use Illuminate\Http\JsonResponse; use App\Http\Controllers\Controller; use App\Services\Draw\DrawAdminActionService; @@ -15,12 +17,12 @@ final class DrawCancelController extends Controller private readonly DrawAdminActionService $service, ) {} - public function __invoke(Draw $draw): JsonResponse + public function __invoke(Request $request, Draw $draw): JsonResponse { try { $cancelled = $this->service->cancelBeforeResult($draw); - } catch (\RuntimeException) { - return ApiResponse::error(trans('api.client_error'), ErrorCode::ClientHttpError->value, null, 409); + } catch (\RuntimeException $e) { + return ApiMessage::runtimeErrorResponse($request, $e); } return ApiResponse::success([ diff --git a/app/Http/Controllers/Api/V1/Admin/Draw/DrawManualCloseController.php b/app/Http/Controllers/Api/V1/Admin/Draw/DrawManualCloseController.php index 3cfea64..58ad200 100644 --- a/app/Http/Controllers/Api/V1/Admin/Draw/DrawManualCloseController.php +++ b/app/Http/Controllers/Api/V1/Admin/Draw/DrawManualCloseController.php @@ -4,7 +4,9 @@ namespace App\Http\Controllers\Api\V1\Admin\Draw; use App\Models\Draw; use App\Lottery\ErrorCode; +use App\Support\ApiMessage; use App\Support\ApiResponse; +use Illuminate\Http\Request; use Illuminate\Http\JsonResponse; use App\Http\Controllers\Controller; use App\Services\Draw\DrawAdminActionService; @@ -15,12 +17,12 @@ final class DrawManualCloseController extends Controller private readonly DrawAdminActionService $service, ) {} - public function __invoke(Draw $draw): JsonResponse + public function __invoke(Request $request, Draw $draw): JsonResponse { try { $closed = $this->service->manualClose($draw); - } catch (\RuntimeException) { - return ApiResponse::error(trans('api.client_error'), ErrorCode::ClientHttpError->value, null, 409); + } catch (\RuntimeException $e) { + return ApiMessage::runtimeErrorResponse($request, $e); } return ApiResponse::success([ diff --git a/app/Http/Controllers/Api/V1/Admin/Draw/DrawManualResultBatchStoreController.php b/app/Http/Controllers/Api/V1/Admin/Draw/DrawManualResultBatchStoreController.php index 84b9966..99a32cf 100644 --- a/app/Http/Controllers/Api/V1/Admin/Draw/DrawManualResultBatchStoreController.php +++ b/app/Http/Controllers/Api/V1/Admin/Draw/DrawManualResultBatchStoreController.php @@ -5,6 +5,7 @@ namespace App\Http\Controllers\Api\V1\Admin\Draw; use App\Models\Draw; use App\Models\AdminUser; use App\Lottery\ErrorCode; +use App\Support\ApiMessage; use App\Support\ApiResponse; use Illuminate\Http\JsonResponse; use App\Http\Controllers\Controller; @@ -31,13 +32,8 @@ final class DrawManualResultBatchStoreController extends Controller try { $batch = $this->service->createPendingBatch($draw, $admin, $request->validated('items')); - } catch (\RuntimeException) { - return ApiResponse::error( - trans('api.client_error', [], $request->lotteryLocale()), - ErrorCode::ClientHttpError->value, - null, - 409, - ); + } catch (\RuntimeException $e) { + return ApiMessage::runtimeErrorResponse($request, $e); } $draw->refresh(); diff --git a/app/Http/Controllers/Api/V1/Admin/Draw/DrawReopenController.php b/app/Http/Controllers/Api/V1/Admin/Draw/DrawReopenController.php index ce7cb8a..d00d48b 100644 --- a/app/Http/Controllers/Api/V1/Admin/Draw/DrawReopenController.php +++ b/app/Http/Controllers/Api/V1/Admin/Draw/DrawReopenController.php @@ -6,6 +6,7 @@ use App\Models\Draw; use App\Models\AdminUser; use App\Lottery\ErrorCode; use App\Support\ApiResponse; +use App\Support\ApiMessage; use Illuminate\Http\JsonResponse; use App\Http\Controllers\Controller; use App\Services\Draw\DrawReopenService; @@ -39,13 +40,8 @@ final class DrawReopenController extends Controller try { $reopened = $this->service->reopenCooldownDraw($draw, $admin, $request->validated('reason') ?? null); - } catch (\RuntimeException) { - return ApiResponse::error( - trans('api.client_error', [], $request->lotteryLocale()), - ErrorCode::ClientHttpError->value, - null, - 409, - ); + } catch (\RuntimeException $e) { + return ApiMessage::runtimeErrorResponse($request, $e); } return ApiResponse::success([ diff --git a/app/Http/Controllers/Api/V1/Admin/Draw/DrawResultBatchPublishController.php b/app/Http/Controllers/Api/V1/Admin/Draw/DrawResultBatchPublishController.php index aa4a5d5..f46614d 100644 --- a/app/Http/Controllers/Api/V1/Admin/Draw/DrawResultBatchPublishController.php +++ b/app/Http/Controllers/Api/V1/Admin/Draw/DrawResultBatchPublishController.php @@ -6,6 +6,7 @@ use App\Models\Draw; use App\Models\AdminUser; use App\Lottery\ErrorCode; use App\Support\ApiResponse; +use App\Support\ApiMessage; use Illuminate\Http\Request; use App\Models\DrawResultBatch; use Illuminate\Http\JsonResponse; @@ -44,13 +45,8 @@ final class DrawResultBatchPublishController extends Controller try { $this->publishService->publishManualBatch($batch, $admin); - } catch (\RuntimeException) { - return ApiResponse::error( - trans('api.client_error', [], $request->lotteryLocale()), - ErrorCode::ClientHttpError->value, - null, - 409, - ); + } catch (\RuntimeException $e) { + return ApiMessage::runtimeErrorResponse($request, $e); } $draw->refresh(); diff --git a/app/Http/Controllers/Api/V1/Admin/Draw/DrawRngRunController.php b/app/Http/Controllers/Api/V1/Admin/Draw/DrawRngRunController.php index b46ac2d..ea4f231 100644 --- a/app/Http/Controllers/Api/V1/Admin/Draw/DrawRngRunController.php +++ b/app/Http/Controllers/Api/V1/Admin/Draw/DrawRngRunController.php @@ -5,6 +5,7 @@ namespace App\Http\Controllers\Api\V1\Admin\Draw; use App\Models\Draw; use App\Lottery\ErrorCode; use App\Support\ApiResponse; +use App\Support\ApiMessage; use App\Lottery\DrawStatus; use Illuminate\Http\JsonResponse; use App\Http\Controllers\Controller; @@ -32,8 +33,8 @@ final class DrawRngRunController extends Controller return $this->rng->executeLocked($locked); }); - } catch (\RuntimeException) { - return ApiResponse::error(trans('api.client_error'), ErrorCode::ClientHttpError->value, null, 409); + } catch (\RuntimeException $e) { + return ApiMessage::runtimeErrorResponse(request(), $e); } $draw->refresh(); diff --git a/app/Http/Controllers/Api/V1/Admin/Integration/AdminIntegrationSiteConnectivityTestController.php b/app/Http/Controllers/Api/V1/Admin/Integration/AdminIntegrationSiteConnectivityTestController.php index d6041c8..564555a 100644 --- a/app/Http/Controllers/Api/V1/Admin/Integration/AdminIntegrationSiteConnectivityTestController.php +++ b/app/Http/Controllers/Api/V1/Admin/Integration/AdminIntegrationSiteConnectivityTestController.php @@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api\V1\Admin\Integration; use App\Models\AdminSite; use App\Models\Player; +use App\Support\ApiMessage; use App\Support\ApiResponse; use App\Lottery\ErrorCode; use Illuminate\Http\Request; @@ -24,7 +25,7 @@ final class AdminIntegrationSiteConnectivityTestController extends Controller abort_if($admin === null, 401); if (! AdminIntegrationSiteAccess::canAccess($admin, $admin_site)) { - return ApiResponse::error('无权访问该站点', ErrorCode::AdminForbidden->value, null, 403); + return ApiMessage::errorResponse($request, 'admin.site_access_denied', ErrorCode::AdminForbidden->value, null, 403); } $sitePlayerId = trim((string) $request->validated('site_player_id')); diff --git a/app/Http/Controllers/Api/V1/Admin/Integration/AdminIntegrationSiteExportController.php b/app/Http/Controllers/Api/V1/Admin/Integration/AdminIntegrationSiteExportController.php index f0d5870..e9396a2 100644 --- a/app/Http/Controllers/Api/V1/Admin/Integration/AdminIntegrationSiteExportController.php +++ b/app/Http/Controllers/Api/V1/Admin/Integration/AdminIntegrationSiteExportController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers\Api\V1\Admin\Integration; use App\Models\AdminSite; +use App\Support\ApiMessage; use App\Support\ApiResponse; use App\Lottery\ErrorCode; use Illuminate\Http\Request; @@ -20,7 +21,7 @@ final class AdminIntegrationSiteExportController extends Controller abort_if($admin === null, 401); if (! AdminIntegrationSiteAccess::canAccess($admin, $admin_site)) { - return ApiResponse::error('无权访问该站点', ErrorCode::AdminForbidden->value, null, 403); + return ApiMessage::errorResponse($request, 'admin.site_access_denied', ErrorCode::AdminForbidden->value, null, 403); } $sheet = AdminIntegrationSitePresenter::parameterSheet($admin_site); diff --git a/app/Http/Controllers/Api/V1/Admin/Integration/AdminIntegrationSiteRotateSecretsController.php b/app/Http/Controllers/Api/V1/Admin/Integration/AdminIntegrationSiteRotateSecretsController.php index 302cb96..1872c19 100644 --- a/app/Http/Controllers/Api/V1/Admin/Integration/AdminIntegrationSiteRotateSecretsController.php +++ b/app/Http/Controllers/Api/V1/Admin/Integration/AdminIntegrationSiteRotateSecretsController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers\Api\V1\Admin\Integration; use App\Models\AdminSite; +use App\Support\ApiMessage; use App\Support\ApiResponse; use App\Lottery\ErrorCode; use Illuminate\Http\Request; @@ -25,7 +26,7 @@ final class AdminIntegrationSiteRotateSecretsController extends Controller abort_if($admin === null, 401); if (! AdminIntegrationSiteAccess::canAccess($admin, $admin_site)) { - return ApiResponse::error('无权操作该站点', ErrorCode::AdminForbidden->value, null, 403); + return ApiMessage::errorResponse($request, 'admin.site_rotate_denied', ErrorCode::AdminForbidden->value, null, 403); } $result = $service->rotateSecrets($admin_site); diff --git a/app/Http/Controllers/Api/V1/Admin/Integration/AdminIntegrationSiteShowController.php b/app/Http/Controllers/Api/V1/Admin/Integration/AdminIntegrationSiteShowController.php index 28e3378..76f64fb 100644 --- a/app/Http/Controllers/Api/V1/Admin/Integration/AdminIntegrationSiteShowController.php +++ b/app/Http/Controllers/Api/V1/Admin/Integration/AdminIntegrationSiteShowController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers\Api\V1\Admin\Integration; use App\Models\AdminSite; +use App\Support\ApiMessage; use App\Support\ApiResponse; use App\Lottery\ErrorCode; use Illuminate\Http\Request; @@ -19,7 +20,7 @@ final class AdminIntegrationSiteShowController extends Controller abort_if($admin === null, 401); if (! AdminIntegrationSiteAccess::canAccess($admin, $admin_site)) { - return ApiResponse::error('无权访问该站点', ErrorCode::AdminForbidden->value, null, 403); + return ApiMessage::errorResponse($request, 'admin.site_access_denied', ErrorCode::AdminForbidden->value, null, 403); } return ApiResponse::success(AdminIntegrationSitePresenter::detail($admin_site)); diff --git a/app/Http/Controllers/Api/V1/Admin/Integration/AdminIntegrationSiteUpdateController.php b/app/Http/Controllers/Api/V1/Admin/Integration/AdminIntegrationSiteUpdateController.php index f05aeee..36ef583 100644 --- a/app/Http/Controllers/Api/V1/Admin/Integration/AdminIntegrationSiteUpdateController.php +++ b/app/Http/Controllers/Api/V1/Admin/Integration/AdminIntegrationSiteUpdateController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers\Api\V1\Admin\Integration; use App\Models\AdminSite; +use App\Support\ApiMessage; use App\Support\ApiResponse; use App\Lottery\ErrorCode; use Illuminate\Http\Request; @@ -26,7 +27,7 @@ final class AdminIntegrationSiteUpdateController extends Controller abort_if($admin === null, 401); if (! AdminIntegrationSiteAccess::canAccess($admin, $admin_site)) { - return ApiResponse::error('无权修改该站点', ErrorCode::AdminForbidden->value, null, 403); + return ApiMessage::errorResponse($request, 'admin.site_update_denied', ErrorCode::AdminForbidden->value, null, 403); } $before = AdminIntegrationSitePresenter::detail($admin_site); diff --git a/app/Http/Controllers/Api/V1/Admin/Jackpot/AdminJackpotPoolAdjustController.php b/app/Http/Controllers/Api/V1/Admin/Jackpot/AdminJackpotPoolAdjustController.php index 63833fd..80c13c0 100644 --- a/app/Http/Controllers/Api/V1/Admin/Jackpot/AdminJackpotPoolAdjustController.php +++ b/app/Http/Controllers/Api/V1/Admin/Jackpot/AdminJackpotPoolAdjustController.php @@ -6,6 +6,7 @@ use App\Models\JackpotPool; use App\Models\AdminUser; use App\Lottery\ErrorCode; use App\Support\ApiResponse; +use App\Support\ApiMessage; use Illuminate\Http\JsonResponse; use App\Http\Controllers\Controller; use App\Services\Jackpot\JackpotPoolAdjustmentService; @@ -44,14 +45,7 @@ final class AdminJackpotPoolAdjustController extends Controller $request, ); } catch (\RuntimeException $e) { - $msg = match ($e->getMessage()) { - 'adjustment_delta_zero' => trans('jackpot.adjustment_delta_zero', [], $request->lotteryLocale()), - 'adjustment_reason_required' => trans('jackpot.adjustment_reason_required', [], $request->lotteryLocale()), - 'adjustment_would_make_balance_negative' => trans('jackpot.adjustment_negative_balance', [], $request->lotteryLocale()), - default => trans('api.client_error', [], $request->lotteryLocale()), - }; - - return ApiResponse::error($msg, ErrorCode::ClientHttpError->value, ['reason' => $e->getMessage()], 422); + return ApiMessage::runtimeErrorResponse($request, $e, ErrorCode::ClientHttpError->value, 422); } $pool->refresh(); diff --git a/app/Http/Controllers/Api/V1/Admin/Jackpot/AdminJackpotPoolManualBurstController.php b/app/Http/Controllers/Api/V1/Admin/Jackpot/AdminJackpotPoolManualBurstController.php index f86e247..c7b17e2 100644 --- a/app/Http/Controllers/Api/V1/Admin/Jackpot/AdminJackpotPoolManualBurstController.php +++ b/app/Http/Controllers/Api/V1/Admin/Jackpot/AdminJackpotPoolManualBurstController.php @@ -5,6 +5,7 @@ namespace App\Http\Controllers\Api\V1\Admin\Jackpot; use App\Models\AdminUser; use App\Models\JackpotPool; use App\Support\ApiResponse; +use App\Support\ApiMessage; use App\Lottery\ErrorCode; use Illuminate\Http\Request; use Illuminate\Http\JsonResponse; @@ -46,7 +47,9 @@ final class AdminJackpotPoolManualBurstController extends Controller $payload = $this->service->execute($pool, (int) $data['draw_id']); } catch (\RuntimeException $e) { return ApiResponse::error( - trans('api.jackpot_manual_burst_failed', ['reason' => $e->getMessage()], $request->lotteryLocale()), + ApiMessage::get($request, 'jackpot_manual_burst_failed', [ + 'reason' => ApiMessage::reason($request, $e->getMessage()), + ]), ErrorCode::ClientHttpError->value, ['reason' => $e->getMessage()], 409, diff --git a/app/Http/Controllers/Api/V1/Admin/Player/AdminPlayerDestroyController.php b/app/Http/Controllers/Api/V1/Admin/Player/AdminPlayerDestroyController.php index 1e463aa..9ea454b 100644 --- a/app/Http/Controllers/Api/V1/Admin/Player/AdminPlayerDestroyController.php +++ b/app/Http/Controllers/Api/V1/Admin/Player/AdminPlayerDestroyController.php @@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api\V1\Admin\Player; use App\Models\Player; use App\Lottery\ErrorCode; +use App\Support\ApiMessage; use App\Support\ApiResponse; use Illuminate\Http\Request; use Illuminate\Http\JsonResponse; @@ -29,12 +30,7 @@ final class AdminPlayerDestroyController extends Controller ->exists(); if ($hasWallets) { - return ApiResponse::error( - '该玩家钱包仍有余额,请先清空后再删除', - ErrorCode::ValidationFailed->value, - null, - 422, - ); + return ApiMessage::errorResponse($request, 'admin.player_wallet_balance_blocks_delete', ErrorCode::ValidationFailed->value, null, 422); } $hasTickets = Player::query() @@ -43,12 +39,7 @@ final class AdminPlayerDestroyController extends Controller ->exists(); if ($hasTickets) { - return ApiResponse::error( - '该玩家存在注单记录,无法删除', - ErrorCode::ValidationFailed->value, - null, - 422, - ); + return ApiMessage::errorResponse($request, 'admin.player_has_tickets_blocks_delete', ErrorCode::ValidationFailed->value, null, 422); } $player->wallets()->delete(); diff --git a/app/Http/Controllers/Api/V1/Admin/Player/AdminPlayerStoreController.php b/app/Http/Controllers/Api/V1/Admin/Player/AdminPlayerStoreController.php index 3eb202f..59f6b63 100644 --- a/app/Http/Controllers/Api/V1/Admin/Player/AdminPlayerStoreController.php +++ b/app/Http/Controllers/Api/V1/Admin/Player/AdminPlayerStoreController.php @@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api\V1\Admin\Player; use App\Models\Player; use App\Lottery\ErrorCode; +use App\Support\ApiMessage; use App\Support\ApiResponse; use Illuminate\Http\JsonResponse; use App\Support\AdminSiteScope; @@ -21,12 +22,7 @@ final class AdminPlayerStoreController extends Controller $siteCode = (string) $request->validated('site_code'); if (! AdminSiteScope::siteCodeAllowed($admin, $siteCode)) { - return ApiResponse::error( - '无权在该站点下创建玩家', - ErrorCode::AdminForbidden->value, - null, - 403, - ); + return ApiMessage::errorResponse($request, 'admin.player_create_site_forbidden', ErrorCode::AdminForbidden->value, null, 403); } $exists = Player::query() @@ -35,12 +31,7 @@ final class AdminPlayerStoreController extends Controller ->exists(); if ($exists) { - return ApiResponse::error( - '该主站玩家已在彩票平台注册', - ErrorCode::ValidationFailed->value, - null, - 422, - ); + return ApiMessage::errorResponse($request, 'admin.player_already_registered', ErrorCode::ValidationFailed->value, null, 422); } $player = Player::query()->create([ diff --git a/app/Http/Controllers/Api/V1/Admin/Risk/AdminRiskPoolManualStatusController.php b/app/Http/Controllers/Api/V1/Admin/Risk/AdminRiskPoolManualStatusController.php index a044c11..e91dff7 100644 --- a/app/Http/Controllers/Api/V1/Admin/Risk/AdminRiskPoolManualStatusController.php +++ b/app/Http/Controllers/Api/V1/Admin/Risk/AdminRiskPoolManualStatusController.php @@ -5,7 +5,9 @@ namespace App\Http\Controllers\Api\V1\Admin\Risk; use App\Models\Draw; use App\Models\RiskPool; use App\Lottery\ErrorCode; +use App\Support\ApiMessage; use App\Support\ApiResponse; +use Illuminate\Http\Request; use App\Models\RiskPoolLockLog; use Illuminate\Http\JsonResponse; use Illuminate\Support\Facades\DB; @@ -17,26 +19,26 @@ final class AdminRiskPoolManualStatusController extends Controller public function __construct( private readonly RiskPoolService $riskPoolService, ) {} - public function close(Draw $draw, string $number_4d): JsonResponse + public function close(Request $request, Draw $draw, string $number_4d): JsonResponse { $pool = $this->updateStatus($draw, $number_4d, true, 'close', 'admin_manual_close'); if ($pool === null) { - return ApiResponse::error(trans('api.not_found'), ErrorCode::ClientHttpError->value, null, 404); + return ApiMessage::errorResponse($request, 'not_found', ErrorCode::ClientHttpError->value, null, 404); } return ApiResponse::success($this->row($pool)); } - public function recover(Draw $draw, string $number_4d): JsonResponse + public function recover(Request $request, Draw $draw, string $number_4d): JsonResponse { $pool = $this->updateStatus($draw, $number_4d, false, 'recover', 'admin_manual_recover'); if ($pool === null) { - return ApiResponse::error(trans('api.not_found'), ErrorCode::ClientHttpError->value, null, 404); + return ApiMessage::errorResponse($request, 'not_found', ErrorCode::ClientHttpError->value, null, 404); } if ((int) $pool->remaining_amount <= 0) { - return ApiResponse::error(trans('api.client_error'), ErrorCode::ClientHttpError->value, [ + return ApiMessage::errorResponse($request, 'risk_pool_no_remaining_amount', ErrorCode::ClientHttpError->value, [ 'reason' => 'risk_pool_no_remaining_amount', ], 409); } diff --git a/app/Http/Controllers/Api/V1/Admin/Settlement/AdminSettlementBatchApproveController.php b/app/Http/Controllers/Api/V1/Admin/Settlement/AdminSettlementBatchApproveController.php index 1910f10..b918685 100644 --- a/app/Http/Controllers/Api/V1/Admin/Settlement/AdminSettlementBatchApproveController.php +++ b/app/Http/Controllers/Api/V1/Admin/Settlement/AdminSettlementBatchApproveController.php @@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api\V1\Admin\Settlement; use App\Models\AdminUser; use App\Lottery\ErrorCode; +use App\Support\ApiMessage; use App\Support\ApiResponse; use App\Models\SettlementBatch; use Illuminate\Http\JsonResponse; @@ -24,8 +25,8 @@ final class AdminSettlementBatchApproveController extends Controller try { $updated = $this->service->approve($batch, $admin, $request->validated('remark') ?? null); - } catch (\RuntimeException) { - return ApiResponse::error(trans('api.client_error'), ErrorCode::ClientHttpError->value, null, 409); + } catch (\RuntimeException $e) { + return ApiMessage::runtimeErrorResponse($request, $e); } return ApiResponse::success([ diff --git a/app/Http/Controllers/Api/V1/Admin/Settlement/AdminSettlementBatchPayoutController.php b/app/Http/Controllers/Api/V1/Admin/Settlement/AdminSettlementBatchPayoutController.php index 5cc293e..f9ed97b 100644 --- a/app/Http/Controllers/Api/V1/Admin/Settlement/AdminSettlementBatchPayoutController.php +++ b/app/Http/Controllers/Api/V1/Admin/Settlement/AdminSettlementBatchPayoutController.php @@ -3,7 +3,9 @@ namespace App\Http\Controllers\Api\V1\Admin\Settlement; use App\Lottery\ErrorCode; +use App\Support\ApiMessage; use App\Support\ApiResponse; +use Illuminate\Http\Request; use App\Models\SettlementBatch; use Illuminate\Http\JsonResponse; use App\Http\Controllers\Controller; @@ -13,18 +15,12 @@ final class AdminSettlementBatchPayoutController extends Controller { public function __construct(private readonly SettlementBatchWorkflowService $service) {} - public function __invoke(SettlementBatch $batch): JsonResponse + public function __invoke(Request $request, SettlementBatch $batch): JsonResponse { try { $updated = $this->service->payout($batch); } catch (\RuntimeException $e) { - $reason = $e->getMessage(); - $msg = match ($reason) { - 'settlement_not_approved' => trans('api.settlement_not_approved'), - default => trans('api.client_error'), - }; - - return ApiResponse::error($msg, ErrorCode::ClientHttpError->value, ['reason' => $reason], 409); + return ApiMessage::runtimeErrorResponse($request, $e); } return ApiResponse::success([ diff --git a/app/Http/Controllers/Api/V1/Admin/Settlement/AdminSettlementBatchRejectController.php b/app/Http/Controllers/Api/V1/Admin/Settlement/AdminSettlementBatchRejectController.php index da673e9..30c3d81 100644 --- a/app/Http/Controllers/Api/V1/Admin/Settlement/AdminSettlementBatchRejectController.php +++ b/app/Http/Controllers/Api/V1/Admin/Settlement/AdminSettlementBatchRejectController.php @@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api\V1\Admin\Settlement; use App\Models\AdminUser; use App\Lottery\ErrorCode; +use App\Support\ApiMessage; use App\Support\ApiResponse; use App\Models\SettlementBatch; use Illuminate\Http\JsonResponse; @@ -24,8 +25,8 @@ final class AdminSettlementBatchRejectController extends Controller try { $updated = $this->service->reject($batch, $admin, $request->validated('remark') ?? null); - } catch (\RuntimeException) { - return ApiResponse::error(trans('api.client_error'), ErrorCode::ClientHttpError->value, null, 409); + } catch (\RuntimeException $e) { + return ApiMessage::runtimeErrorResponse($request, $e); } return ApiResponse::success([ diff --git a/app/Http/Controllers/Api/V1/Admin/User/AdminRoleDestroyController.php b/app/Http/Controllers/Api/V1/Admin/User/AdminRoleDestroyController.php index 60b6238..aeb3261 100644 --- a/app/Http/Controllers/Api/V1/Admin/User/AdminRoleDestroyController.php +++ b/app/Http/Controllers/Api/V1/Admin/User/AdminRoleDestroyController.php @@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api\V1\Admin\User; use App\Models\AdminRole; use App\Lottery\ErrorCode; +use App\Support\ApiMessage; use App\Support\ApiResponse; use Illuminate\Http\Request; use App\Services\AuditLogger; @@ -16,13 +17,13 @@ final class AdminRoleDestroyController extends Controller public function __invoke(Request $request, AdminRole $admin_role): JsonResponse { if ($admin_role->slug === AdminRole::ROLE_SUPER_ADMIN) { - return ApiResponse::error('不能删除超级管理员角色', ErrorCode::ValidationFailed->value, null, 422); + return ApiMessage::errorResponse($request, 'admin.role_cannot_delete_super_admin', ErrorCode::ValidationFailed->value, null, 422); } if ((bool) $admin_role->is_system) { - return ApiResponse::error('系统内置角色不允许删除', ErrorCode::ValidationFailed->value, null, 422); + return ApiMessage::errorResponse($request, 'admin.role_builtin_cannot_delete', ErrorCode::ValidationFailed->value, null, 422); } if ($admin_role->assignedUserCount() > 0) { - return ApiResponse::error('该角色下仍有关联管理员,不能删除', ErrorCode::ValidationFailed->value, null, 422); + return ApiMessage::errorResponse($request, 'admin.role_has_users_cannot_delete', ErrorCode::ValidationFailed->value, null, 422); } $before = AdminRoleApiPresenter::item($admin_role); diff --git a/app/Http/Controllers/Api/V1/Admin/User/AdminUserDestroyController.php b/app/Http/Controllers/Api/V1/Admin/User/AdminUserDestroyController.php index ad79ba4..b9e7954 100644 --- a/app/Http/Controllers/Api/V1/Admin/User/AdminUserDestroyController.php +++ b/app/Http/Controllers/Api/V1/Admin/User/AdminUserDestroyController.php @@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api\V1\Admin\User; use App\Models\AdminUser; use App\Lottery\ErrorCode; +use App\Support\ApiMessage; use App\Support\ApiResponse; use Illuminate\Http\Request; use App\Services\AuditLogger; @@ -20,12 +21,7 @@ final class AdminUserDestroyController extends Controller $actor = $request->lotteryAdmin(); if ((int) $actor->getKey() === (int) $admin_user->getKey()) { - return ApiResponse::error( - '不能删除当前登录账号', - ErrorCode::ValidationFailed->value, - null, - 422, - ); + return ApiMessage::errorResponse($request, 'admin.user_cannot_delete_self', ErrorCode::ValidationFailed->value, null, 422); } $admin_user->load('roles'); @@ -35,12 +31,7 @@ final class AdminUserDestroyController extends Controller ->whereHas('roles', static fn ($q) => $q->where('admin_roles.slug', AdminUser::ROLE_SUPER_ADMIN)) ->exists(); if (! $hasOther) { - return ApiResponse::error( - '不能删除最后一个超级管理员', - ErrorCode::ValidationFailed->value, - null, - 422, - ); + return ApiMessage::errorResponse($request, 'admin.user_cannot_delete_last_super_admin', ErrorCode::ValidationFailed->value, null, 422); } } diff --git a/app/Http/Controllers/Api/V1/Admin/User/Concerns/EnsuresSuperAdminActor.php b/app/Http/Controllers/Api/V1/Admin/User/Concerns/EnsuresSuperAdminActor.php index 33fa1fa..d6356b0 100644 --- a/app/Http/Controllers/Api/V1/Admin/User/Concerns/EnsuresSuperAdminActor.php +++ b/app/Http/Controllers/Api/V1/Admin/User/Concerns/EnsuresSuperAdminActor.php @@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api\V1\Admin\User\Concerns; use App\Models\AdminUser; use App\Lottery\ErrorCode; +use App\Support\ApiMessage; use App\Support\ApiResponse; use Illuminate\Http\Request; use Illuminate\Http\JsonResponse; @@ -15,12 +16,7 @@ trait EnsuresSuperAdminActor /** @var AdminUser $actor */ $actor = $request->lotteryAdmin(); if (! $actor->isSuperAdmin()) { - return ApiResponse::error( - '仅超级管理员可管理角色', - ErrorCode::AdminForbidden->value, - null, - 403, - ); + return ApiMessage::errorResponse($request, 'admin.super_admin_only_for_roles', ErrorCode::AdminForbidden->value, null, 403); } return null; diff --git a/app/Http/Controllers/Api/V1/Admin/Wallet/TransferOrderReconcileController.php b/app/Http/Controllers/Api/V1/Admin/Wallet/TransferOrderReconcileController.php index 0be1496..f1cfd8b 100644 --- a/app/Http/Controllers/Api/V1/Admin/Wallet/TransferOrderReconcileController.php +++ b/app/Http/Controllers/Api/V1/Admin/Wallet/TransferOrderReconcileController.php @@ -2,7 +2,9 @@ namespace App\Http\Controllers\Api\V1\Admin\Wallet; +use App\Lottery\ErrorCode; use App\Models\TransferOrder; +use App\Support\ApiMessage; use App\Support\ApiResponse; use App\Support\LotteryMessage; use App\Exceptions\WalletOperationException; @@ -27,7 +29,7 @@ final class TransferOrderReconcileController extends Controller { $order = TransferOrder::query()->where('transfer_no', $transferNo)->first(); if ($order === null) { - return ApiResponse::error(__('wallet.order_not_found'), 404); + return ApiMessage::errorResponse($request, 'wallet.order_not_found', ErrorCode::ClientHttpError->value, null, 404); } try { @@ -52,7 +54,7 @@ final class TransferOrderReconcileController extends Controller { $order = TransferOrder::query()->where('transfer_no', $transferNo)->first(); if ($order === null) { - return ApiResponse::error(__('wallet.order_not_found'), 404); + return ApiMessage::errorResponse($request, 'wallet.order_not_found', ErrorCode::ClientHttpError->value, null, 404); } try { @@ -77,7 +79,7 @@ final class TransferOrderReconcileController extends Controller { $order = TransferOrder::query()->where('transfer_no', $transferNo)->first(); if ($order === null) { - return ApiResponse::error(__('wallet.order_not_found'), 404); + return ApiMessage::errorResponse($request, 'wallet.order_not_found', ErrorCode::ClientHttpError->value, null, 404); } try { diff --git a/app/Http/Controllers/Api/V1/Play/PlayEffectiveCatalogController.php b/app/Http/Controllers/Api/V1/Play/PlayEffectiveCatalogController.php index fbb6f36..fa09ce1 100644 --- a/app/Http/Controllers/Api/V1/Play/PlayEffectiveCatalogController.php +++ b/app/Http/Controllers/Api/V1/Play/PlayEffectiveCatalogController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers\Api\V1\Play; use App\Lottery\ErrorCode; +use App\Support\ApiMessage; use App\Support\ApiResponse; use Illuminate\Http\Request; use Illuminate\Http\JsonResponse; @@ -23,20 +24,10 @@ final class PlayEffectiveCatalogController extends Controller try { return ApiResponse::success($catalog->build($c)); } catch (ModelNotFoundException) { - return ApiResponse::error( - 'effective config not initialized', - ErrorCode::NotFound->value, - null, - 404, - ); + return ApiMessage::errorResponse($request, 'effective_config_not_initialized', ErrorCode::NotFound->value, null, 404); } catch (\InvalidArgumentException $e) { if ($e->getMessage() === 'currency') { - return ApiResponse::error( - 'invalid or disabled currency', - ErrorCode::ConfigCurrencyInvalid->value, - null, - 400, - ); + return ApiMessage::errorResponse($request, 'invalid_or_disabled_currency', ErrorCode::ConfigCurrencyInvalid->value, null, 400); } throw $e; diff --git a/app/Http/Controllers/Api/V1/Setting/SettingIndexController.php b/app/Http/Controllers/Api/V1/Setting/SettingIndexController.php index f8a174e..bfe7040 100644 --- a/app/Http/Controllers/Api/V1/Setting/SettingIndexController.php +++ b/app/Http/Controllers/Api/V1/Setting/SettingIndexController.php @@ -5,6 +5,7 @@ namespace App\Http\Controllers\Api\V1\Setting; use App\Http\Controllers\Controller; use App\Models\LotterySetting; use App\Lottery\ErrorCode; +use App\Support\ApiMessage; use App\Support\ApiResponse; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; @@ -25,7 +26,8 @@ final class SettingIndexController extends Controller $group = $request->query('group'); if (! is_string($group) || $group === '' || ! in_array($group, self::PUBLIC_GROUPS, true)) { - return ApiResponse::error( + return ApiMessage::errorResponse( + $request, 'invalid_settings_group', ErrorCode::ClientHttpError->value, ['allowed_groups' => self::PUBLIC_GROUPS], diff --git a/app/Http/Controllers/Api/V1/Wallet/WalletBalanceController.php b/app/Http/Controllers/Api/V1/Wallet/WalletBalanceController.php index 9cd0780..9aa89e4 100644 --- a/app/Http/Controllers/Api/V1/Wallet/WalletBalanceController.php +++ b/app/Http/Controllers/Api/V1/Wallet/WalletBalanceController.php @@ -5,6 +5,7 @@ namespace App\Http\Controllers\Api\V1\Wallet; use App\Models\Player; use App\Lottery\ErrorCode; use App\Models\PlayerWallet; +use App\Support\ApiMessage; use App\Support\ApiResponse; use Illuminate\Http\Request; use App\Support\CurrencyResolver; @@ -88,7 +89,7 @@ final class WalletBalanceController extends Controller if (! CurrencyResolver::isEnabled($code)) { return ApiResponse::error( - __('wallet.invalid_currency'), + ApiMessage::get($request, 'wallet.invalid_currency'), ErrorCode::WalletInvalidCurrency->value, null, 400, diff --git a/app/Http/Controllers/Api/V1/Wallet/WalletTransferInController.php b/app/Http/Controllers/Api/V1/Wallet/WalletTransferInController.php index dfdcaa0..d9ebbbe 100644 --- a/app/Http/Controllers/Api/V1/Wallet/WalletTransferInController.php +++ b/app/Http/Controllers/Api/V1/Wallet/WalletTransferInController.php @@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api\V1\Wallet; use App\Models\Player; use App\Lottery\ErrorCode; +use App\Support\ApiMessage; use App\Support\ApiResponse; use App\Support\LotteryMessage; use App\Support\CurrencyResolver; @@ -59,7 +60,7 @@ final class WalletTransferInController extends Controller if (! CurrencyResolver::isEnabled($code)) { return ApiResponse::error( - __('wallet.invalid_currency'), + ApiMessage::get($request, 'wallet.invalid_currency'), ErrorCode::WalletInvalidCurrency->value, null, 400, diff --git a/app/Http/Controllers/Api/V1/Wallet/WalletTransferOutController.php b/app/Http/Controllers/Api/V1/Wallet/WalletTransferOutController.php index 406153d..1c041fd 100644 --- a/app/Http/Controllers/Api/V1/Wallet/WalletTransferOutController.php +++ b/app/Http/Controllers/Api/V1/Wallet/WalletTransferOutController.php @@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api\V1\Wallet; use App\Models\Player; use App\Lottery\ErrorCode; +use App\Support\ApiMessage; use App\Support\ApiResponse; use App\Support\LotteryMessage; use App\Support\CurrencyResolver; @@ -59,7 +60,7 @@ final class WalletTransferOutController extends Controller if (! CurrencyResolver::isEnabled($code)) { return ApiResponse::error( - __('wallet.invalid_currency'), + ApiMessage::get($request, 'wallet.invalid_currency'), ErrorCode::WalletInvalidCurrency->value, null, 400, diff --git a/app/Http/Middleware/EnsureAdminApiResourcePermission.php b/app/Http/Middleware/EnsureAdminApiResourcePermission.php index 27139ec..34af301 100644 --- a/app/Http/Middleware/EnsureAdminApiResourcePermission.php +++ b/app/Http/Middleware/EnsureAdminApiResourcePermission.php @@ -5,6 +5,7 @@ namespace App\Http\Middleware; use Closure; use App\Models\AdminUser; use App\Lottery\ErrorCode; +use App\Support\ApiMessage; use App\Support\ApiResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; @@ -27,7 +28,7 @@ final class EnsureAdminApiResourcePermission $route = $request->route(); $routeName = is_object($route) ? $route->getName() : null; if (! is_string($routeName) || $routeName === '') { - return ApiResponse::error('后台路由缺少 route name,无法执行资源鉴权。', ErrorCode::InternalError->value, null, 500); + return ApiMessage::errorResponse($request, 'admin.route_name_missing_for_permission', ErrorCode::InternalError->value, null, 500); } $normalizedRouteName = $this->normalizeAdminRouteName($routeName); @@ -37,11 +38,13 @@ final class EnsureAdminApiResourcePermission ->first(['id', 'code', 'auth_mode']); if ($resource === null) { - return ApiResponse::error( - sprintf('后台 API 资源未配置:%s', $normalizedRouteName), + return ApiMessage::errorResponse( + $request, + 'admin.api_resource_not_configured', ErrorCode::InternalError->value, ['route_name' => $normalizedRouteName], 500, + ['route' => $normalizedRouteName], ); } @@ -59,11 +62,13 @@ final class EnsureAdminApiResourcePermission ->all(); if ($permissionCodes === []) { - return ApiResponse::error( - sprintf('后台 API 资源未绑定权限动作:%s', (string) $resource->code), + return ApiMessage::errorResponse( + $request, + 'admin.api_resource_no_permission_binding', ErrorCode::InternalError->value, ['resource_code' => $resource->code], 500, + ['code' => (string) $resource->code], ); } diff --git a/app/Support/AdminMessage.php b/app/Support/AdminMessage.php index 1965947..2025352 100644 --- a/app/Support/AdminMessage.php +++ b/app/Support/AdminMessage.php @@ -20,19 +20,9 @@ final class AdminMessage * * @param string $key 语言包键名,如 'unauthenticated', 'permission_denied' */ - public static function get(Request $request, string $key): string + public static function get(Request $request, string $key, array $replace = []): string { - $fallback = (string) config('lottery.locales.fallback', 'en'); - $locale = (string) ($request->attributes->get('lottery_locale') ?? LotteryLocale::resolve($request)); - - $fullKey = 'admin.'.$key; - $msg = trans($fullKey, [], $locale); - - if ($msg !== $fullKey) { - return $msg; - } - - return trans($fullKey, [], $fallback); + return ApiMessage::get($request, 'admin.'.$key, $replace); } /** diff --git a/app/Support/AdminSiteScope.php b/app/Support/AdminSiteScope.php index 3802fc6..84df4a7 100644 --- a/app/Support/AdminSiteScope.php +++ b/app/Support/AdminSiteScope.php @@ -6,6 +6,8 @@ use App\Models\AdminSite; use App\Models\AdminUser; use App\Models\Player; use App\Lottery\ErrorCode; +use App\Support\ApiMessage; +use App\Support\ApiResponse; use Illuminate\Database\Eloquent\Builder; use Illuminate\Http\JsonResponse; @@ -147,7 +149,13 @@ final class AdminSiteScope public static function denyUnlessPlayerAccessible(AdminUser $admin, Player $player): ?JsonResponse { if (! self::playerAccessible($admin, $player)) { - return ApiResponse::error('无权访问该站点下的玩家', ErrorCode::AdminForbidden->value, null, 403); + return ApiMessage::errorResponse( + request(), + 'admin.site_player_access_denied', + ErrorCode::AdminForbidden->value, + null, + 403, + ); } return null; diff --git a/app/Support/ApiMessage.php b/app/Support/ApiMessage.php new file mode 100644 index 0000000..dd21de8 --- /dev/null +++ b/app/Support/ApiMessage.php @@ -0,0 +1,140 @@ + */ + private const LOOKUP_PREFIXES = ['api.reasons.', 'api.', 'admin.', 'jackpot.', 'wallet.']; + + public static function locale(?Request $request = null): string + { + $request ??= request(); + + if ($request instanceof Request && $request->attributes->has('lottery_locale')) { + return (string) $request->attributes->get('lottery_locale'); + } + + return LotteryLocale::resolve($request instanceof Request ? $request : null); + } + + /** + * @param array $replace + */ + public static function get(?Request $request, string $key, array $replace = []): string + { + $locale = self::locale($request); + $fallback = (string) config('lottery.locales.fallback', 'en'); + + foreach ([$locale, $fallback] as $tryLocale) { + foreach (self::candidateKeys($key) as $fullKey) { + $msg = trans($fullKey, $replace, $tryLocale); + if ($msg !== $fullKey && $msg !== '') { + return $msg; + } + } + } + + return $key; + } + + /** + * RuntimeException / 业务 reason 机器码 → 用户可见文案。 + * + * @param array $replace + */ + public static function reason(?Request $request, string $reasonKey, array $replace = []): string + { + $reasonKey = trim($reasonKey); + if ($reasonKey === '') { + return self::get($request, 'client_error', $replace); + } + + $translated = self::get($request, $reasonKey, $replace); + if ($translated !== $reasonKey) { + return $translated; + } + + return self::get($request, 'client_error', $replace); + } + + public static function successMessage(?Request $request = null): string + { + return self::get($request, 'success.ok'); + } + + /** + * @param array $replace + */ + public static function errorResponse( + ?Request $request, + string $messageKey, + int $code, + mixed $data = null, + int $httpStatus = 400, + array $replace = [], + ): JsonResponse { + return ApiResponse::error( + self::get($request, $messageKey, $replace), + $code, + $data, + $httpStatus, + ); + } + + public static function runtimeErrorResponse( + ?Request $request, + \RuntimeException $exception, + int $code = 0, + int $httpStatus = 409, + ): JsonResponse { + $reason = trim($exception->getMessage()); + $resolvedCode = $code !== 0 ? $code : ErrorCode::ClientHttpError->value; + + return ApiResponse::error( + self::reason($request, $reason), + $resolvedCode, + ['reason' => $reason], + $httpStatus, + ); + } + + /** + * @return list + */ + private static function candidateKeys(string $key): array + { + $key = trim($key); + if ($key === '') { + return ['api.client_error']; + } + + if (str_contains($key, '.')) { + return array_values(array_unique([ + $key, + 'api.'.$key, + 'admin.'.$key, + 'jackpot.'.$key, + 'wallet.'.$key, + 'api.reasons.'.$key, + ])); + } + + $keys = []; + foreach (self::LOOKUP_PREFIXES as $prefix) { + $keys[] = $prefix.$key; + } + + return $keys; + } +} diff --git a/app/Support/ApiResponse.php b/app/Support/ApiResponse.php index 980117f..171306f 100644 --- a/app/Support/ApiResponse.php +++ b/app/Support/ApiResponse.php @@ -4,6 +4,7 @@ namespace App\Support; use App\Lottery\ErrorCode; use Illuminate\Http\JsonResponse; +use Illuminate\Http\Request; /** * 对外 API 统一 JSON 结构:{ code, msg, data }。 @@ -13,11 +14,11 @@ use Illuminate\Http\JsonResponse; */ final class ApiResponse { - public static function success(mixed $data = null, string $msg = 'ok', int $code = 0): JsonResponse + public static function success(mixed $data = null, ?string $msg = null, int $code = 0, ?Request $request = null): JsonResponse { return response()->json([ 'code' => $code, - 'msg' => $msg, + 'msg' => $msg ?? ApiMessage::successMessage($request), 'data' => $data, ]); } diff --git a/app/Support/LotteryMessage.php b/app/Support/LotteryMessage.php index 992c81f..f8c32b7 100644 --- a/app/Support/LotteryMessage.php +++ b/app/Support/LotteryMessage.php @@ -22,16 +22,7 @@ final class LotteryMessage */ public static function wallet(Request $request, int $code): string { - $fallback = (string) config('lottery.locales.fallback', 'en'); - $locale = (string) ($request->attributes->get('lottery_locale') ?? LotteryLocale::resolve($request)); - $key = 'wallet.'.$code; - - $msg = trans($key, [], $locale); - if ($msg !== $key) { - return $msg; - } - - return trans($key, [], $fallback); + return ApiMessage::get($request, 'wallet.'.$code); } /** @@ -41,16 +32,6 @@ final class LotteryMessage */ public static function sso(Request $request, int $code): string { - $fallback = (string) config('lottery.locales.fallback', 'en'); - $locale = (string) ($request->attributes->get('lottery_locale') ?? LotteryLocale::resolve($request)); - $key = 'sso.'.$code; // 对应 lang/{locale}/sso.php 内键名,如 '8001' - - $msg = trans($key, [], $locale); - // Laravel 无键时返回整条 key 字符串,此时改用 fallback 语言再试一次 - if ($msg !== $key) { - return $msg; - } - - return trans($key, [], $fallback); + return ApiMessage::get($request, 'sso.'.$code); } } diff --git a/lang/en/admin.php b/lang/en/admin.php index 8f04f6d..7e4b36f 100644 --- a/lang/en/admin.php +++ b/lang/en/admin.php @@ -6,5 +6,25 @@ return [ 'invalid_credentials' => 'Invalid account or password.', 'account_disabled' => 'This account has been disabled.', 'permission_denied' => 'You do not have permission to perform this action.', + 'forbidden' => 'You do not have permission to perform this action.', 'settlement_run_skipped' => 'Settlement was not run for this draw (check draw status and published result batch).', + 'site_access_denied' => 'You do not have access to this site.', + 'site_rotate_denied' => 'You cannot rotate secrets for this site.', + 'site_update_denied' => 'You cannot modify this site.', + 'site_player_access_denied' => 'You do not have access to players under this site.', + 'player_create_site_forbidden' => 'You cannot create players under this site.', + 'player_already_registered' => 'This main-site player is already registered.', + 'player_wallet_balance_blocks_delete' => 'Player wallet still has balance. Clear it before deletion.', + 'player_has_tickets_blocks_delete' => 'Player has ticket records and cannot be deleted.', + 'role_cannot_delete_super_admin' => 'Cannot delete the super admin role.', + 'role_builtin_cannot_delete' => 'Built-in roles cannot be deleted.', + 'role_has_users_cannot_delete' => 'This role still has assigned admins and cannot be 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.', + 'route_name_missing_for_permission' => 'Admin route is missing a route name for permission checks.', + 'api_resource_not_configured' => 'Admin API resource is not configured: :route', + 'api_resource_no_permission_binding' => 'Admin API resource has no permission binding: :code', + 'currency_default_cannot_delete' => 'Default currency cannot be deleted.', + 'currency_referenced_cannot_delete' => 'Currency is referenced by business data and cannot be deleted: :refs', ]; diff --git a/lang/en/api.php b/lang/en/api.php index 95cc5fc..6756a67 100644 --- a/lang/en/api.php +++ b/lang/en/api.php @@ -1,9 +1,16 @@ [ + 'ok' => 'OK', + ], + 'validation_failed' => 'The given data was invalid.', 'client_error' => 'This request could not be completed.', + 'invalid_params' => 'Invalid request parameters.', + 'invalid_settings_group' => 'This settings group is not allowed.', 'draw_no_exists' => 'Draw number already exists. Use another draw number or sequence.', + 'draw_not_found' => 'Draw not found.', 'draw_timeline_invalid' => 'Start time must be before close time, and close time must be before draw time.', 'draw_not_editable' => 'Only pending draws, or open draws with no bets, can be edited.', 'draw_not_deletable' => 'Only pending draws with no bets can be deleted.', @@ -14,4 +21,37 @@ return [ 'too_many_requests' => 'Too many requests. Please try again later.', 'server_error' => 'Something went wrong. Please try again later.', 'jackpot_manual_burst_failed' => 'Manual jackpot burst failed: :reason', + 'config_version_not_draft' => 'This config version is not a draft.', + 'config_version_cannot_delete_active' => 'Cannot delete the active config version.', + 'effective_config_not_initialized' => 'Play configuration has not been initialized.', + 'invalid_or_disabled_currency' => 'Invalid or disabled currency.', + 'risk_pool_no_remaining_amount' => 'Insufficient remaining amount in the risk pool.', + + 'reasons' => [ + 'jackpot_disabled' => 'Jackpot pool is disabled.', + 'jackpot_pool_empty' => 'Jackpot pool balance is zero.', + 'jackpot_already_burst_for_draw' => 'Jackpot was already burst for this draw.', + 'jackpot_manual_no_first_prize_winners' => 'No first-prize winners on this draw.', + 'jackpot_already_allocated_for_draw' => 'Jackpot was already allocated for this draw.', + 'draw_not_ready_for_jackpot_burst' => 'Draw is not in settling or settled status.', + 'draw_result_not_published' => 'Draw results have not been published.', + 'settlement_batch_not_found' => 'Settlement batch not found for this draw.', + 'settlement_not_pending_review' => 'Settlement batch is not pending review.', + 'settlement_not_approved' => 'Settlement batch is not approved.', + 'draw_has_unsettled_tickets' => 'This draw still has unsettled tickets.', + 'batch_not_pending_review' => 'Result batch is not pending review.', + 'draw_not_ready_to_publish' => 'Draw is not ready to publish results.', + 'batch_result_version_stale' => 'Result batch version is stale. Refresh and try again.', + 'draw_settlement_in_progress' => 'Settlement is in progress for this draw.', + 'draw_already_settled' => 'This draw is already settled.', + 'draw_pending_result_batch_exists' => 'A pending result batch already exists.', + 'draw_not_closeable' => 'This draw cannot be closed manually.', + 'draw_not_cancelable' => 'This draw cannot be cancelled.', + 'draw_has_settled_tickets' => 'This draw has settled tickets.', + 'draw_not_runnable' => 'RNG cannot be run for this draw.', + 'draw_not_in_cooldown' => 'Draw is not in cooldown.', + 'adjustment_delta_zero' => 'Adjustment amount cannot be zero.', + 'adjustment_reason_required' => 'Adjustment reason is required.', + 'adjustment_would_make_balance_negative' => 'Adjustment would make pool balance negative.', + ], ]; diff --git a/lang/en/wallet.php b/lang/en/wallet.php index f6d55a8..649546e 100644 --- a/lang/en/wallet.php +++ b/lang/en/wallet.php @@ -3,6 +3,7 @@ /** PRD 钱包段多语言;NegotiateLotteryLocale 后由 __() 选用 */ return [ 'invalid_currency' => 'Invalid currency code', + 'order_not_found' => 'Transfer order not found', '1001' => 'Insufficient lottery wallet balance', '1002' => 'A previous transfer is still processing; please retry shortly', diff --git a/lang/ne/admin.php b/lang/ne/admin.php index 7c69dae..74fe05e 100644 --- a/lang/ne/admin.php +++ b/lang/ne/admin.php @@ -1,10 +1,30 @@ 'प्रमाणीकरण छैन वा सेसन समाप्त भएको छ।', - 'invalid_captcha' => 'क्याप्चा गलत वा समय सकियो।', - 'invalid_credentials' => 'खाता वा पासवर्ड गलत।', + 'unauthenticated' => 'लगइन छैन वा सत्र समाप्त भयो।', + 'invalid_captcha' => 'क्याप्चा गलत वा म्याद सकिएको छ।', + 'invalid_credentials' => 'खाता वा पासवर्ड गलत छ।', 'account_disabled' => 'यो खाता निष्क्रिय गरिएको छ।', - 'permission_denied' => 'यो कार्य गर्न अनुमति छैन।', - 'settlement_run_skipped' => 'यस ड्रका लागि बन्दोबस्त चलाइएन (ड्र स्थिति र प्रकाशित नतिजा जाँच गर्नुहोस्)।', + 'permission_denied' => 'यो कार्य गर्ने अनुमति छैन।', + 'forbidden' => 'यो कार्य गर्ने अनुमति छैन।', + 'settlement_run_skipped' => 'यो ड्रको सेटलमेन्ट चलाइएन (ड्र स्थिति र प्रकाशित नतिजा जाँच गर्नुहोस्)।', + 'site_access_denied' => 'यो साइटमा पहुँच छैन।', + 'site_rotate_denied' => 'यो साइटको गोप्यियता परिवर्तन गर्न मिल्दैन।', + 'site_update_denied' => 'यो साइट सम्पादन गर्न मिल्दैन।', + 'site_player_access_denied' => 'यो साइटका खेलाडीहरूमा पहुँच छैन।', + 'player_create_site_forbidden' => 'यो साइटमा खेलाडी सिर्जना गर्न मिल्दैन।', + 'player_already_registered' => 'यो मुख्य साइट खेलाडी पहिले नै दर्ता भइसकेको छ।', + 'player_wallet_balance_blocks_delete' => 'खेलाडी वालेटमा ब्यालेन्स छ, मेटाउनु अघि खाली गर्नुहोस्।', + 'player_has_tickets_blocks_delete' => 'खेलाडीसँग टिकट रेकर्ड छ, मेटाउन मिल्दैन।', + 'role_cannot_delete_super_admin' => 'सुपर एडमिन भूमिका मेटाउन मिल्दैन।', + 'role_builtin_cannot_delete' => 'बिल्ट-इन भूमिका मेटाउन मिल्दैन।', + 'role_has_users_cannot_delete' => 'यो भूमिकामा अझै एडमिन छ, मेटाउन मिल्दैन।', + 'user_cannot_delete_self' => 'आफ्नै खाता मेटाउन मिल्दैन।', + 'user_cannot_delete_last_super_admin' => 'अन्तिम सुपर एडमिन मेटाउन मिल्दैन।', + 'super_admin_only_for_roles' => 'भूमिका व्यवस्थापन केवल सुपर एडमिनले गर्न सक्छ।', + 'route_name_missing_for_permission' => 'एडमिन रुटमा route name छैन, अनुमति जाँच गर्न सकिँदैन।', + 'api_resource_not_configured' => 'एडमिन API स्रोत कन्फिग गरिएको छैन: :route', + 'api_resource_no_permission_binding' => 'एडमिन API स्रोतमा अनुमति बाइन्डिङ छैन: :code', + 'currency_default_cannot_delete' => 'पूर्वनिर्धारित मुद्रा मेटाउन मिल्दैन।', + 'currency_referenced_cannot_delete' => 'मुद्रा व्यवसाय डाटामा प्रयोग भएको छ, मेटाउन मिल्दैन: :refs', ]; diff --git a/lang/ne/api.php b/lang/ne/api.php index 5793c73..731b7f2 100644 --- a/lang/ne/api.php +++ b/lang/ne/api.php @@ -1,9 +1,16 @@ [ + 'ok' => 'सफल', + ], + 'validation_failed' => 'दिइएको डाटा अमान्य छ।', 'client_error' => 'यो अनुरोध पूरा गर्न सकिएन।', + 'invalid_params' => 'अनुरोध प्यारामिटर अमान्य छ।', + 'invalid_settings_group' => 'यो सेटिङ समूह पढ्न अनुमति छैन।', 'draw_no_exists' => 'यो ड्र नम्बर पहिले नै छ। अर्को ड्र नम्बर वा क्रम प्रयोग गर्नुहोस्।', + 'draw_not_found' => 'ड्र फेला परेन।', 'draw_timeline_invalid' => 'सुरु समय बन्द समय भन्दा अघि, बन्द समय ड्र समय भन्दा अघि हुनुपर्छ।', 'draw_not_editable' => 'केवल pending वा बेट नभएको open ड्र सम्पादन गर्न सकिन्छ।', 'draw_not_deletable' => 'केवल pending र बेट नभएको ड्र मेटाउन सकिन्छ।', @@ -14,4 +21,37 @@ return [ 'too_many_requests' => 'धेरै अनुरोधहरू। कृपया पछि प्रयास गर्नुहोस्।', 'server_error' => 'केही गडबड भयो। कृपया पछि प्रयास गर्नुहोस्।', 'jackpot_manual_burst_failed' => 'म्यानुअल ज्याकपोट बर्स्ट असफल: :reason', + 'config_version_not_draft' => 'यो कन्फिग संस्करण ड्राफ्ट होइन।', + 'config_version_cannot_delete_active' => 'सक्रिय कन्फिग संस्करण मेटाउन मिल्दैन।', + 'effective_config_not_initialized' => 'प्ले कन्फिग अझै सुरु भएको छैन।', + 'invalid_or_disabled_currency' => 'मुद्रा अमान्य वा निष्क्रिय छ।', + 'risk_pool_no_remaining_amount' => 'जोखिम पोलमा बाँकी रकम अपर्याप्त छ।', + + 'reasons' => [ + 'jackpot_disabled' => 'ज्याकपोट पोल निष्क्रिय छ।', + 'jackpot_pool_empty' => 'ज्याकपोट पोल ब्यालेन्स शून्य छ।', + 'jackpot_already_burst_for_draw' => 'यो ड्रमा पहिले नै बर्स्ट भइसकेको छ।', + 'jackpot_manual_no_first_prize_winners' => 'यो ड्रमा पहिलो पुरस्कार विजेता छैन।', + 'jackpot_already_allocated_for_draw' => 'यो ड्रमा ज्याकपोट पहिले नै बाँडिएको छ।', + 'draw_not_ready_for_jackpot_burst' => 'ड्र settling वा settled अवस्थामा छैन।', + 'draw_result_not_published' => 'ड्र नतिजा प्रकाशित भएको छैन।', + 'settlement_batch_not_found' => 'यो ड्रको सेटलमेन्ट ब्याच फेला परेन।', + 'settlement_not_pending_review' => 'सेटलमेन्ट ब्याच समीक्षामा छैन।', + 'settlement_not_approved' => 'सेटलमेन्ट ब्याच स्वीकृत छैन।', + 'draw_has_unsettled_tickets' => 'यो ड्रमा अझै नसेटल टिकट छ।', + 'batch_not_pending_review' => 'नतिजा ब्याच समीक्षामा छैन।', + 'draw_not_ready_to_publish' => 'ड्र नतिजा प्रकाशित गर्न तयार छैन।', + 'batch_result_version_stale' => 'नतिजा संस्करण पुरानो भयो। रिफ्रेस गरेर पुन: प्रयास गर्नुहोस्।', + 'draw_settlement_in_progress' => 'यो ड्रको सेटलमेन्ट चलिरहेको छ।', + 'draw_already_settled' => 'यो ड्र पहिले नै सेटल भइसकेको छ।', + 'draw_pending_result_batch_exists' => 'पेन्डिङ नतिजा ब्याच पहिले नै छ।', + 'draw_not_closeable' => 'यो ड्र म्यानुअल बन्द गर्न मिल्दैन।', + 'draw_not_cancelable' => 'यो ड्र रद्द गर्न मिल्दैन।', + 'draw_has_settled_tickets' => 'यो ड्रमा सेटल भएका टिकट छ।', + 'draw_not_runnable' => 'यो ड्रमा RNG चलाउन मिल्दैन।', + 'draw_not_in_cooldown' => 'ड्र कूलडाउन अवस्थामा छैन।', + 'adjustment_delta_zero' => 'समायोजन रकम ० हुन सक्दैन।', + 'adjustment_reason_required' => 'समायोजन कारण आवश्यक छ।', + 'adjustment_would_make_balance_negative' => 'समायोजन पछि पोल ब्यालेन्स ऋणात्मक हुन्छ।', + ], ]; diff --git a/lang/ne/wallet.php b/lang/ne/wallet.php index a576f6c..077552b 100644 --- a/lang/ne/wallet.php +++ b/lang/ne/wallet.php @@ -2,6 +2,7 @@ return [ 'invalid_currency' => 'मुद्रा कोड अमान्य', + 'order_not_found' => 'स्थानान्तरण अर्डर फेला परेन', '1001' => 'लटरी वालेट ब्यालेन्स अपर्याप्त', '1002' => 'अघिल्लो स्थानान्तरण अझै प्रक्रियामा छ, पछि प्रयास गर्नुहोस्', diff --git a/lang/zh/admin.php b/lang/zh/admin.php index 4eba6e6..014d021 100644 --- a/lang/zh/admin.php +++ b/lang/zh/admin.php @@ -6,5 +6,25 @@ return [ 'invalid_credentials' => '账号或密码错误。', 'account_disabled' => '该账号已被禁用。', 'permission_denied' => '当前账号无此操作权限。', + 'forbidden' => '当前账号无此操作权限。', 'settlement_run_skipped' => '本期未执行结算(请检查期号状态与已发布开奖批次)。', + 'site_access_denied' => '无权访问该站点。', + 'site_rotate_denied' => '无权操作该站点。', + 'site_update_denied' => '无权修改该站点。', + 'site_player_access_denied' => '无权访问该站点下的玩家。', + 'player_create_site_forbidden' => '无权在该站点下创建玩家。', + 'player_already_registered' => '该主站玩家已在彩票平台注册。', + 'player_wallet_balance_blocks_delete' => '该玩家钱包仍有余额,请先清空后再删除。', + 'player_has_tickets_blocks_delete' => '该玩家存在注单记录,无法删除。', + 'role_cannot_delete_super_admin' => '不能删除超级管理员角色。', + 'role_builtin_cannot_delete' => '系统内置角色不允许删除。', + 'role_has_users_cannot_delete' => '该角色下仍有关联管理员,不能删除。', + 'user_cannot_delete_self' => '不能删除当前登录账号。', + 'user_cannot_delete_last_super_admin' => '不能删除最后一个超级管理员。', + 'super_admin_only_for_roles' => '仅超级管理员可管理角色。', + 'route_name_missing_for_permission' => '后台路由缺少 route name,无法执行资源鉴权。', + 'api_resource_not_configured' => '后台 API 资源未配置::route', + 'api_resource_no_permission_binding' => '后台 API 资源未绑定权限动作::code', + 'currency_default_cannot_delete' => '默认币种不可删除。', + 'currency_referenced_cannot_delete' => '该币种已被业务数据引用,暂不可删除::refs', ]; diff --git a/lang/zh/api.php b/lang/zh/api.php index da12b85..a6f48e7 100644 --- a/lang/zh/api.php +++ b/lang/zh/api.php @@ -1,9 +1,16 @@ [ + 'ok' => '操作成功', + ], + 'validation_failed' => '请求参数校验未通过。', 'client_error' => '请求无法完成。', + 'invalid_params' => '请求参数无效。', + 'invalid_settings_group' => '不允许读取该配置分组。', 'draw_no_exists' => '期号已存在,请更换期号或流水号。', + 'draw_not_found' => '期号不存在。', 'draw_timeline_invalid' => '开始时间须早于封盘时间,封盘时间须早于开奖时间。', 'draw_not_editable' => '仅「未开始」或「可下注且无注单」的期号可编辑时间。', 'draw_not_deletable' => '仅「未开始」且无注单的期号可删除。', @@ -14,4 +21,37 @@ return [ 'too_many_requests' => '请求过于频繁,请稍后再试。', 'server_error' => '服务暂时不可用,请稍后再试。', 'jackpot_manual_burst_failed' => '手动爆池失败::reason', + 'config_version_not_draft' => '配置版本不是草稿状态,无法执行该操作。', + 'config_version_cannot_delete_active' => '不能删除当前生效中的配置版本。', + 'effective_config_not_initialized' => '玩法配置尚未初始化。', + 'invalid_or_disabled_currency' => '币种无效或未启用。', + 'risk_pool_no_remaining_amount' => '风险池剩余额度不足。', + + 'reasons' => [ + 'jackpot_disabled' => '奖池已停用。', + 'jackpot_pool_empty' => '奖池余额为 0,无法爆池。', + 'jackpot_already_burst_for_draw' => '该期已执行过爆池。', + 'jackpot_manual_no_first_prize_winners' => '该期没有头奖中奖注单,无法手动爆池。', + 'jackpot_already_allocated_for_draw' => '该期已分配过奖池派彩。', + 'draw_not_ready_for_jackpot_burst' => '期号尚未进入结算中或已结算,无法手动爆池。', + 'draw_result_not_published' => '该期开奖结果尚未发布。', + 'settlement_batch_not_found' => '未找到该期的结算批次。', + 'settlement_not_pending_review' => '结算批次不在待审核状态。', + 'settlement_not_approved' => '结算批次尚未审核通过。', + 'draw_has_unsettled_tickets' => '该期仍有未结算注单。', + 'batch_not_pending_review' => '开奖结果批次不在待审核状态。', + 'draw_not_ready_to_publish' => '期号状态不允许发布开奖结果。', + 'batch_result_version_stale' => '开奖结果版本已过期,请刷新后重试。', + 'draw_settlement_in_progress' => '该期正在结算中,无法发布结果。', + 'draw_already_settled' => '该期已结算完成。', + 'draw_pending_result_batch_exists' => '该期已有待审核的开奖批次。', + 'draw_not_closeable' => '当前期号状态不允许手动封盘。', + 'draw_not_cancelable' => '当前期号状态不允许取消。', + 'draw_has_settled_tickets' => '该期已有已结算注单,无法执行此操作。', + 'draw_not_runnable' => '当前期号不可执行 RNG 开奖。', + 'draw_not_in_cooldown' => '期号不在冷静期,无法重开。', + 'adjustment_delta_zero' => '调整金额不能为 0。', + 'adjustment_reason_required' => '请填写调整原因。', + 'adjustment_would_make_balance_negative' => '调整后奖池余额不能为负数。', + ], ]; diff --git a/lang/zh/wallet.php b/lang/zh/wallet.php index a0706c6..965eebe 100644 --- a/lang/zh/wallet.php +++ b/lang/zh/wallet.php @@ -2,6 +2,7 @@ return [ 'invalid_currency' => '币种参数不合法', + 'order_not_found' => '转账订单不存在', '1001' => '彩票钱包余额不足', '1002' => '上一笔转账仍在处理中,请稍后重试', diff --git a/tests/Feature/AdminUserPermissionApiTest.php b/tests/Feature/AdminUserPermissionApiTest.php index cbd2afd..2181f5d 100644 --- a/tests/Feature/AdminUserPermissionApiTest.php +++ b/tests/Feature/AdminUserPermissionApiTest.php @@ -329,10 +329,11 @@ test('admin cannot delete self', function (): void { $me = AdminUser::query()->where('username', 'self_guard')->firstOrFail(); $this->withHeader('Authorization', 'Bearer '.$token) + ->withHeader('X-Locale', 'zh') ->deleteJson('/api/v1/admin/admin-users/'.$me->id) ->assertStatus(422) ->assertJsonPath('code', ErrorCode::ValidationFailed->value) - ->assertJsonPath('msg', '不能删除当前登录账号'); + ->assertJsonPath('msg', '不能删除当前登录账号。'); }); test('admin cannot delete the last super admin', function (): void { @@ -348,9 +349,10 @@ test('admin cannot delete the last super admin', function (): void { grantSuperAdminRole($s1); $this->withHeader('Authorization', 'Bearer '.$token) + ->withHeader('X-Locale', 'zh') ->deleteJson('/api/v1/admin/admin-users/'.$s1->id) ->assertStatus(422) - ->assertJsonPath('msg', '不能删除最后一个超级管理员'); + ->assertJsonPath('msg', '不能删除最后一个超级管理员。'); $s2 = AdminUser::query()->create([ 'username' => 'super_two', @@ -362,11 +364,13 @@ test('admin cannot delete the last super admin', function (): void { grantSuperAdminRole($s2); $this->withHeader('Authorization', 'Bearer '.$token) + ->withHeader('X-Locale', 'zh') ->deleteJson('/api/v1/admin/admin-users/'.$s1->id) ->assertOk(); $this->withHeader('Authorization', 'Bearer '.$token) + ->withHeader('X-Locale', 'zh') ->deleteJson('/api/v1/admin/admin-users/'.$s2->id) ->assertStatus(422) - ->assertJsonPath('msg', '不能删除最后一个超级管理员'); + ->assertJsonPath('msg', '不能删除最后一个超级管理员。'); }); diff --git a/tests/Feature/ApiMessageTest.php b/tests/Feature/ApiMessageTest.php new file mode 100644 index 0000000..297fe8c --- /dev/null +++ b/tests/Feature/ApiMessageTest.php @@ -0,0 +1,30 @@ +attributes->set('lottery_locale', 'zh'); + + expect(ApiMessage::reason($request, 'draw_not_ready_for_jackpot_burst')) + ->toBe('期号尚未进入结算中或已结算,无法手动爆池。'); +}); + +test('api message resolves success ok in en locale', function (): void { + $request = Request::create('/api/v1/test', 'GET'); + $request->attributes->set('lottery_locale', 'en'); + + expect(ApiMessage::successMessage($request))->toBe('OK'); +}); + +test('api message resolves admin key', function (): void { + $request = Request::create('/api/v1/test', 'GET'); + $request->attributes->set('lottery_locale', 'zh'); + + expect(ApiMessage::get($request, 'admin.site_access_denied')) + ->toBe('无权访问该站点。'); +});